Commit 9c77b941 by Ravi Varadarajan

remove redundant rtl dir from Flows/<Enablement>/<Testcase>

Signed-off-by: Ravi Varadarajan <rvaradarajan@ucsd.edu>
parent 217e2d99
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Description: Scrambles the address in such a way, that part of the memory is accessed
// sequentially and part is interleaved.
// Current constraints:
// Author: Samuel Riedel <sriedel@iis.ee.ethz.ch>
module address_scrambler #(
parameter int unsigned AddrWidth = 32,
parameter int unsigned ByteOffset = 2,
parameter int unsigned NumTiles = 2,
parameter int unsigned NumBanksPerTile = 2,
parameter bit Bypass = 0,
parameter int unsigned SeqMemSizePerTile = 4*1024
) (
input logic [AddrWidth-1:0] address_i,
output logic [AddrWidth-1:0] address_o
);
localparam int unsigned BankOffsetBits = $clog2(NumBanksPerTile);
localparam int unsigned TileIdBits = $clog2(NumTiles);
localparam int unsigned SeqPerTileBits = $clog2(SeqMemSizePerTile);
localparam int unsigned SeqTotalBits = SeqPerTileBits+TileIdBits;
localparam int unsigned ConstantBitsLSB = ByteOffset + BankOffsetBits;
localparam int unsigned ScrambleBits = SeqPerTileBits-ConstantBitsLSB;
if (Bypass || NumTiles < 2) begin
assign address_o = address_i;
end else begin
logic [ScrambleBits-1:0] scramble; // Address bits that have to be shuffled around
logic [TileIdBits-1:0] tile_id; // Which tile does this address region belong to
// Leave this part of the address unchanged
// The LSBs that correspond to the offset inside a tile. These are the byte offset (bank width)
// and the Bank offset (Number of Banks in tile)
assign address_o[ConstantBitsLSB-1:0] = address_i[ConstantBitsLSB-1:0];
// The MSBs that are outside of the sequential memory size. Currently the sequential memory size
// always starts at 0. These are all the MSBs up to SeqMemSizePerTile*NumTiles
assign address_o[AddrWidth-1:SeqTotalBits] = address_i[AddrWidth-1:SeqTotalBits];
// Scramble the middle part
// Bits that would have gone to different tiles but now go to increasing lines in the same tile
assign scramble = address_i[SeqPerTileBits-1:ConstantBitsLSB]; // Bits that would
// Bits that would have gone to increasing lines in the same tile but now go to different tiles
assign tile_id = address_i[SeqTotalBits-1:SeqPerTileBits];
always_comb begin
// Default: Unscrambled
address_o[SeqTotalBits-1:ConstantBitsLSB] = {tile_id, scramble};
// If not in bypass mode and address is in sequential region and more than one tile
if (address_i < (NumTiles * SeqMemSizePerTile)) begin
address_o[SeqTotalBits-1:ConstantBitsLSB] = {scramble, tile_id};
end
end
end
// Check for unsupported configurations
if (NumBanksPerTile < 2)
$fatal(1, "NumBanksPerTile must be greater than 2. The special case '1' is currently not supported!");
if (SeqMemSizePerTile % (2**ByteOffset*NumBanksPerTile) != 0)
$fatal(1, "SeqMemSizePerTile must be a multiple of BankWidth*NumBanksPerTile!");
endmodule : address_scrambler
// Copyright (c) 2014-2018 ETH Zurich, 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.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
/// An AXI4 cut.
///
/// Breaks all combinatorial paths between its input and output.
module axi_cut #(
// bypass enable
parameter bit Bypass = 1'b0,
// AXI channel structs
parameter type aw_chan_t = logic,
parameter type w_chan_t = logic,
parameter type b_chan_t = logic,
parameter type ar_chan_t = logic,
parameter type r_chan_t = logic,
// AXI request & response structs
parameter type req_t = logic,
parameter type resp_t = logic
) (
input logic clk_i,
input logic rst_ni,
// salve port
input req_t slv_req_i,
output resp_t slv_resp_o,
// master port
output req_t mst_req_o,
input resp_t mst_resp_i
);
// a spill register for each channel
spill_register #(
.T ( aw_chan_t ),
.Bypass ( Bypass )
) i_reg_aw (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_req_i.aw_valid ),
.ready_o ( slv_resp_o.aw_ready ),
.data_i ( slv_req_i.aw ),
.valid_o ( mst_req_o.aw_valid ),
.ready_i ( mst_resp_i.aw_ready ),
.data_o ( mst_req_o.aw )
);
spill_register #(
.T ( w_chan_t ),
.Bypass ( Bypass )
) i_reg_w (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_req_i.w_valid ),
.ready_o ( slv_resp_o.w_ready ),
.data_i ( slv_req_i.w ),
.valid_o ( mst_req_o.w_valid ),
.ready_i ( mst_resp_i.w_ready ),
.data_o ( mst_req_o.w )
);
spill_register #(
.T ( b_chan_t ),
.Bypass ( Bypass )
) i_reg_b (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_resp_i.b_valid ),
.ready_o ( mst_req_o.b_ready ),
.data_i ( mst_resp_i.b ),
.valid_o ( slv_resp_o.b_valid ),
.ready_i ( slv_req_i.b_ready ),
.data_o ( slv_resp_o.b )
);
spill_register #(
.T ( ar_chan_t ),
.Bypass ( Bypass )
) i_reg_ar (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_req_i.ar_valid ),
.ready_o ( slv_resp_o.ar_ready ),
.data_i ( slv_req_i.ar ),
.valid_o ( mst_req_o.ar_valid ),
.ready_i ( mst_resp_i.ar_ready ),
.data_o ( mst_req_o.ar )
);
spill_register #(
.T ( r_chan_t ),
.Bypass ( Bypass )
) i_reg_r (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_resp_i.r_valid ),
.ready_o ( mst_req_o.r_ready ),
.data_i ( mst_resp_i.r ),
.valid_o ( slv_resp_o.r_valid ),
.ready_i ( slv_req_i.r_ready ),
.data_o ( slv_resp_o.r )
);
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
// interface wrapper
module axi_cut_intf #(
// Bypass eneable
parameter bit BYPASS = 1'b0,
// The address width.
parameter int unsigned ADDR_WIDTH = 0,
// The data width.
parameter int unsigned DATA_WIDTH = 0,
// The ID width.
parameter int unsigned ID_WIDTH = 0,
// The user data width.
parameter int unsigned USER_WIDTH = 0
) (
input logic clk_i ,
input logic rst_ni ,
AXI_BUS.Slave in ,
AXI_BUS.Master out
);
typedef logic [ID_WIDTH-1:0] id_t;
typedef logic [ADDR_WIDTH-1:0] addr_t;
typedef logic [DATA_WIDTH-1:0] data_t;
typedef logic [DATA_WIDTH/8-1:0] strb_t;
typedef logic [USER_WIDTH-1:0] user_t;
`AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t)
`AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t)
`AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t)
req_t slv_req, mst_req;
resp_t slv_resp, mst_resp;
`AXI_ASSIGN_TO_REQ(slv_req, in)
`AXI_ASSIGN_FROM_RESP(in, slv_resp)
`AXI_ASSIGN_FROM_REQ(out, mst_req)
`AXI_ASSIGN_TO_RESP(mst_resp, out)
axi_cut #(
.Bypass ( BYPASS ),
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.req_t ( req_t ),
.resp_t ( resp_t )
) i_axi_cut (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// Check the invariants.
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (ADDR_WIDTH > 0) else $fatal(1, "Wrong addr width parameter");
assert (DATA_WIDTH > 0) else $fatal(1, "Wrong data width parameter");
assert (ID_WIDTH > 0) else $fatal(1, "Wrong id width parameter");
assert (USER_WIDTH > 0) else $fatal(1, "Wrong user width parameter");
assert (in.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_ID_WIDTH == ID_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_USER_WIDTH == USER_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ID_WIDTH == ID_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_USER_WIDTH == USER_WIDTH) else $fatal(1, "Wrong interface definition");
end
`endif
// pragma translate_on
endmodule
module axi_lite_cut_intf #(
// bypass enable
parameter bit BYPASS = 1'b0,
/// The address width.
parameter int unsigned ADDR_WIDTH = 0,
/// The data width.
parameter int unsigned DATA_WIDTH = 0
) (
input logic clk_i ,
input logic rst_ni ,
AXI_LITE.Slave in ,
AXI_LITE.Master out
);
typedef logic [ADDR_WIDTH-1:0] addr_t;
typedef logic [DATA_WIDTH-1:0] data_t;
typedef logic [DATA_WIDTH/8-1:0] strb_t;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t)
`AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t)
`AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t)
`AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t)
`AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t)
req_t slv_req, mst_req;
resp_t slv_resp, mst_resp;
`AXI_LITE_ASSIGN_TO_REQ(slv_req, in)
`AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp)
`AXI_LITE_ASSIGN_FROM_REQ(out, mst_req)
`AXI_LITE_ASSIGN_TO_RESP(mst_resp, out)
axi_cut #(
.Bypass ( BYPASS ),
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.req_t ( req_t ),
.resp_t ( resp_t )
) i_axi_cut (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// Check the invariants.
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (ADDR_WIDTH > 0) else $fatal(1, "Wrong addr width parameter");
assert (DATA_WIDTH > 0) else $fatal(1, "Wrong data width parameter");
assert (in.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
end
`endif
// pragma translate_on
endmodule
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Implement a hierarchical AXI interconnect. Below shows one level of the interconnect. This module
// recursively instantiates itself and creates a tree of interconnects, each with `NumPortsPerMux`
// slave ports.
//
// TODO: Add a configurable cache per level
//
// AXI Mux ID Width
// Converter
// |‾╲
// +-------->| ╲
// | + +-------+
// +-------->| M | | |
// | U |------->| > |--------->
// | X | | |
// | + +-------+
// +-------->| ╱
// |_╱
// Internal
// Slave type type Master type
module axi_hier_interco #(
parameter int unsigned NumSlvPorts = 0,
parameter int unsigned NumPortsPerMux = NumSlvPorts,
parameter int unsigned EnableCache = 1'b0,
parameter int unsigned AddrWidth = 0,
parameter int unsigned DataWidth = 0,
parameter int unsigned SlvIdWidth = 0,
parameter int unsigned MstIdWidth = 0,
parameter int unsigned UserWidth = 0,
parameter type slv_req_t = logic,
parameter type slv_resp_t = logic,
parameter type mst_req_t = logic,
parameter type mst_resp_t = logic
) (
input logic clk_i,
input logic rst_ni,
input logic test_i,
input slv_req_t [NumSlvPorts-1:0] slv_req_i,
output slv_resp_t [NumSlvPorts-1:0] slv_resp_o,
output mst_req_t mst_req_o,
input mst_resp_t mst_resp_i
);
////////////////
// Typedefs //
////////////////
localparam int unsigned IntIdWidth = SlvIdWidth + $clog2(NumSlvPorts);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
typedef logic [SlvIdWidth-1:0] slv_id_t;
typedef logic [MstIdWidth-1:0] mst_id_t;
typedef logic [IntIdWidth-1:0] int_id_t;
typedef logic [UserWidth-1:0] user_t;
`include "axi/typedef.svh"
// Common AXI types
`AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t);
// Slave AXI types
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_t, user_t);
`AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t);
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t);
`AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t);
// Intermediate AXI types
`AXI_TYPEDEF_AW_CHAN_T(int_aw_t, addr_t, int_id_t, user_t);
`AXI_TYPEDEF_B_CHAN_T(int_b_t, int_id_t, user_t);
`AXI_TYPEDEF_AR_CHAN_T(int_ar_t, addr_t, int_id_t, user_t);
`AXI_TYPEDEF_R_CHAN_T(int_r_t, data_t, int_id_t, user_t);
`AXI_TYPEDEF_REQ_T(int_req_t, int_aw_t, w_t, int_ar_t);
`AXI_TYPEDEF_RESP_T(int_resp_t, int_b_t, int_r_t );
///////////////
// Interco //
///////////////
// Recursive module to implement multiple hierarchy levels at once
if (NumSlvPorts <= NumPortsPerMux) begin : gen_axi_level_final
// Intermediate AXI channel
int_req_t int_req;
int_resp_t int_resp;
axi_mux #(
// AXI parameter and channel types
.SlvAxiIDWidth (SlvIdWidth ), // AXI ID width, slave ports
.slv_aw_chan_t (slv_aw_t ), // AW Channel Type, slave ports
.mst_aw_chan_t (int_aw_t ), // AW Channel Type, master port
.w_chan_t (w_t ), // W Channel Type, all ports
.slv_b_chan_t (slv_b_t ), // B Channel Type, slave ports
.mst_b_chan_t (int_b_t ), // B Channel Type, master port
.slv_ar_chan_t (slv_ar_t ), // AR Channel Type, slave ports
.mst_ar_chan_t (int_ar_t ), // AR Channel Type, master port
.slv_r_chan_t (slv_r_t ), // R Channel Type, slave ports
.mst_r_chan_t (int_r_t ), // R Channel Type, master port
.slv_req_t (slv_req_t ), // Slave port request type
.slv_resp_t (slv_resp_t ), // Slave port response type
.mst_req_t (int_req_t ), // Master ports request type
.mst_resp_t (int_resp_t ), // Master ports response type
.NoSlvPorts (NumSlvPorts), // Number of slave ports
// Maximum number of outstanding transactions per write
.MaxWTrans (8 ),
// If enabled, this multiplexer is purely combinatorial
.FallThrough (1'b0 ),
// add spill register on write master ports, adds a cycle latency on write channels
.SpillAw (1'b1 ),
.SpillW (1'b1 ),
.SpillB (1'b1 ),
// add spill register on read master ports, adds a cycle latency on read channels
.SpillAr (1'b1 ),
.SpillR (1'b1 )
) i_axi_mux (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (test_i ),
.slv_reqs_i (slv_req_i ),
.slv_resps_o (slv_resp_o),
.mst_req_o (int_req ),
.mst_resp_i (int_resp )
);
axi_id_remap #(
.AxiSlvPortIdWidth (IntIdWidth),
.AxiSlvPortMaxUniqIds (IntIdWidth),
.AxiMaxTxnsPerId (4 ),
.AxiMstPortIdWidth (MstIdWidth),
.slv_req_t (int_req_t ),
.slv_resp_t (int_resp_t),
.mst_req_t (mst_req_t ),
.mst_resp_t (mst_resp_t)
) i_axi_id_remap (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.slv_req_i (int_req ),
.slv_resp_o (int_resp ),
.mst_req_o (mst_req_o ),
.mst_resp_i (mst_resp_i)
);
// TODO: Implement cache
if (EnableCache[0])
$error("[axi_hier_interco] `EnableCache` not yet supported.");
// Check all the AXI widths
if ($bits(slv_req_i[0].aw.addr) != AddrWidth)
$error("[axi_hier_interco] `slv_req_i.aw.addr` does not match AddrWidth.");
if ($bits(slv_req_i[0].w.data) != DataWidth)
$error("[axi_hier_interco] `slv_req_i.w.data` does not match DataWidth.");
if ($bits(slv_req_i[0].aw.id) != SlvIdWidth)
$error("[axi_hier_interco] `slv_req_i.aw.id` does not match SlvIdWidth.");
if ($bits(slv_req_i[0].aw.user) != UserWidth)
$error("[axi_hier_interco] `slv_req_i.aw.user` does not match UserWidth.");
if ($bits(mst_req_o.aw.addr) != AddrWidth)
$error("[axi_hier_interco] `mst_req_o.aw.addr` does not match AddrWidth.");
if ($bits(mst_req_o.w.data) != DataWidth)
$error("[axi_hier_interco] `mst_req_o.w.data` does not match DataWidth.");
if ($bits(mst_req_o.aw.id) != MstIdWidth)
$error("[axi_hier_interco] `mst_req_o.aw.id` does not match MstIdWidth.");
if ($bits(mst_req_o.aw.user) != UserWidth)
$error("[axi_hier_interco] `mst_req_o.aw.user` does not match UserWidth.");
if ($bits(int_req.aw.addr) != AddrWidth)
$error("[axi_hier_interco] `int_req.aw.addr` does not match AddrWidth.");
if ($bits(int_req.w.data) != DataWidth)
$error("[axi_hier_interco] `int_req.w.data` does not match DataWidth.");
if ($bits(int_req.aw.id) != IntIdWidth)
$error("[axi_hier_interco] `int_req.aw.id` does not match IntIdWidth.");
if ($bits(int_req.aw.user) != UserWidth)
$error("[axi_hier_interco] `int_req.aw.user` does not match UserWidth.");
end else begin : gen_axi_level_recursive
// More than one level missing. --> Recursively call this module
// This level will contain `NumMuxes` interconnects
localparam int unsigned NumMuxes = NumSlvPorts / NumPortsPerMux;
slv_req_t [NumMuxes-1:0] int_req;
slv_resp_t [NumMuxes-1:0] int_resp;
for (genvar i = 0; i < NumMuxes; i++) begin : gen_axi_intercos
axi_hier_interco #(
.NumSlvPorts (NumPortsPerMux),
.NumPortsPerMux (NumPortsPerMux),
.EnableCache (EnableCache[0]),
.AddrWidth (AddrWidth ),
.DataWidth (DataWidth ),
.SlvIdWidth (SlvIdWidth ),
.MstIdWidth (SlvIdWidth ),
.UserWidth (UserWidth ),
.slv_req_t (slv_req_t ),
.slv_resp_t (slv_resp_t ),
.mst_req_t (slv_req_t ),
.mst_resp_t (slv_resp_t )
) i_axi_interco (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (test_i ),
.slv_req_i (slv_req_i[i*NumPortsPerMux +: NumPortsPerMux] ),
.slv_resp_o (slv_resp_o[i*NumPortsPerMux +: NumPortsPerMux]),
.mst_req_o (int_req[i] ),
.mst_resp_i (int_resp[i] )
);
end
axi_hier_interco #(
.NumSlvPorts (NumMuxes ),
.NumPortsPerMux (NumPortsPerMux),
.EnableCache (EnableCache>>1),
.AddrWidth (AddrWidth ),
.DataWidth (DataWidth ),
.SlvIdWidth (SlvIdWidth ),
.MstIdWidth (MstIdWidth ),
.UserWidth (UserWidth ),
.slv_req_t (slv_req_t ),
.slv_resp_t (slv_resp_t ),
.mst_req_t (mst_req_t ),
.mst_resp_t (mst_resp_t )
) i_axi_interco (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (test_i ),
.slv_req_i (int_req ),
.slv_resp_o (int_resp ),
.mst_req_o (mst_req_o ),
.mst_resp_i (mst_resp_i)
);
if (NumMuxes * NumPortsPerMux != NumSlvPorts)
$error("[axi_hier_interco] `NumSlvPorts mod NumPortsPerMux` must be 0.");
end
if (NumPortsPerMux <= 1)
$error("[axi_hier_interco] `NumPortsPerMux` must be bigger than 1.");
endmodule
// Copyright 2019 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.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// AXI ID Prepend: This module prepends/strips the MSB from the AXI IDs.
// Constraints enforced through assertions: ID width of slave and master port
module axi_id_prepend #(
parameter int unsigned NoBus = 1, // Can take multiple axi busses
parameter int unsigned AxiIdWidthSlvPort = 4, // AXI ID Width of the Slave Ports
parameter int unsigned AxiIdWidthMstPort = 6, // AXI ID Width of the Master Ports
parameter type slv_aw_chan_t = logic, // AW Channel Type for slv port
parameter type slv_w_chan_t = logic, // W Channel Type for slv port
parameter type slv_b_chan_t = logic, // B Channel Type for slv port
parameter type slv_ar_chan_t = logic, // AR Channel Type for slv port
parameter type slv_r_chan_t = logic, // R Channel Type for slv port
parameter type mst_aw_chan_t = logic, // AW Channel Type for mst port
parameter type mst_w_chan_t = logic, // W Channel Type for mst port
parameter type mst_b_chan_t = logic, // B Channel Type for mst port
parameter type mst_ar_chan_t = logic, // AR Channel Type for mst port
parameter type mst_r_chan_t = logic, // R Channel Type for mst port
// DEPENDENT PARAMETER DO NOT OVERWRITE!
parameter int unsigned PreIdWidth = AxiIdWidthMstPort - AxiIdWidthSlvPort
) (
input logic [PreIdWidth-1:0] pre_id_i, // ID to be prepended
// slave port (input), connect master modules here
// AW channel
input slv_aw_chan_t [NoBus-1:0] slv_aw_chans_i,
input logic [NoBus-1:0] slv_aw_valids_i,
output logic [NoBus-1:0] slv_aw_readies_o,
// W channel
input slv_w_chan_t [NoBus-1:0] slv_w_chans_i,
input logic [NoBus-1:0] slv_w_valids_i,
output logic [NoBus-1:0] slv_w_readies_o,
// B channel
output slv_b_chan_t [NoBus-1:0] slv_b_chans_o,
output logic [NoBus-1:0] slv_b_valids_o,
input logic [NoBus-1:0] slv_b_readies_i,
// AR channel
input slv_ar_chan_t [NoBus-1:0] slv_ar_chans_i,
input logic [NoBus-1:0] slv_ar_valids_i,
output logic [NoBus-1:0] slv_ar_readies_o,
// R channel
output slv_r_chan_t [NoBus-1:0] slv_r_chans_o,
output logic [NoBus-1:0] slv_r_valids_o,
input logic [NoBus-1:0] slv_r_readies_i,
// master ports (output), connect slave modules here
// AW channel
output mst_aw_chan_t [NoBus-1:0] mst_aw_chans_o,
output logic [NoBus-1:0] mst_aw_valids_o,
input logic [NoBus-1:0] mst_aw_readies_i,
// W channel
output mst_w_chan_t [NoBus-1:0] mst_w_chans_o,
output logic [NoBus-1:0] mst_w_valids_o,
input logic [NoBus-1:0] mst_w_readies_i,
// B channel
input mst_b_chan_t [NoBus-1:0] mst_b_chans_i,
input logic [NoBus-1:0] mst_b_valids_i,
output logic [NoBus-1:0] mst_b_readies_o,
// AR channel
output mst_ar_chan_t [NoBus-1:0] mst_ar_chans_o,
output logic [NoBus-1:0] mst_ar_valids_o,
input logic [NoBus-1:0] mst_ar_readies_i,
// R channel
input mst_r_chan_t [NoBus-1:0] mst_r_chans_i,
input logic [NoBus-1:0] mst_r_valids_i,
output logic [NoBus-1:0] mst_r_readies_o
);
// prepend the ID
for (genvar i = 0; i < NoBus; i++) begin : gen_id_prepend
if (PreIdWidth == 0) begin : gen_no_prepend
assign mst_aw_chans_o[i] = slv_aw_chans_i[i];
assign mst_ar_chans_o[i] = slv_ar_chans_i[i];
end else begin : gen_prepend
always_comb begin
mst_aw_chans_o[i] = slv_aw_chans_i[i];
mst_ar_chans_o[i] = slv_ar_chans_i[i];
mst_aw_chans_o[i].id = {pre_id_i, slv_aw_chans_i[i].id[AxiIdWidthSlvPort-1:0]};
mst_ar_chans_o[i].id = {pre_id_i, slv_ar_chans_i[i].id[AxiIdWidthSlvPort-1:0]};
end
end
// The ID is in the highest bits of the struct, so an assignment from a channel with a wide ID
// to a channel with a shorter ID correctly cuts the prepended ID.
assign slv_b_chans_o[i] = mst_b_chans_i[i];
assign slv_r_chans_o[i] = mst_r_chans_i[i];
end
// assign the handshaking's and w channel
assign mst_w_chans_o = slv_w_chans_i;
assign mst_aw_valids_o = slv_aw_valids_i;
assign slv_aw_readies_o = mst_aw_readies_i;
assign mst_w_valids_o = slv_w_valids_i;
assign slv_w_readies_o = mst_w_readies_i;
assign slv_b_valids_o = mst_b_valids_i;
assign mst_b_readies_o = slv_b_readies_i;
assign mst_ar_valids_o = slv_ar_valids_i;
assign slv_ar_readies_o = mst_ar_readies_i;
assign slv_r_valids_o = mst_r_valids_i;
assign mst_r_readies_o = slv_r_readies_i;
// pragma translate_off
`ifndef VERILATOR
initial begin : p_assert
assert(NoBus > 0)
else $fatal(1, "Input must be at least one element wide.");
assert(PreIdWidth == ($bits(mst_aw_chans_o[0].id) - $bits(slv_aw_chans_i[0].id)))
else $fatal(1, "Prepend ID Width must be: $bits(mst_aw_chans_o.id)-$bits(slv_aw_chans_i.id)");
assert ($bits(mst_aw_chans_o[0].id) > $bits(slv_aw_chans_i[0].id))
else $fatal(1, "The master AXI port has to have a wider ID than the slave port.");
end
aw_id : assert final(
mst_aw_chans_o[0].id[$bits(slv_aw_chans_i[0].id)-1:0] === slv_aw_chans_i[0].id)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_addr : assert final(mst_aw_chans_o[0].addr === slv_aw_chans_i[0].addr)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_len : assert final(mst_aw_chans_o[0].len === slv_aw_chans_i[0].len)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_size : assert final(mst_aw_chans_o[0].size === slv_aw_chans_i[0].size)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_qos : assert final(mst_aw_chans_o[0].qos === slv_aw_chans_i[0].qos)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
b_id : assert final(
mst_b_chans_i[0].id[$bits(slv_b_chans_o[0].id)-1:0] === slv_b_chans_o[0].id)
else $fatal (1, "Something with the B channel ID stripping went wrong.");
b_resp : assert final(mst_b_chans_i[0].resp === slv_b_chans_o[0].resp)
else $fatal (1, "Something with the B channel ID stripping went wrong.");
ar_id : assert final(
mst_ar_chans_o[0].id[$bits(slv_ar_chans_i[0].id)-1:0] === slv_ar_chans_i[0].id)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_addr : assert final(mst_ar_chans_o[0].addr === slv_ar_chans_i[0].addr)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_len : assert final(mst_ar_chans_o[0].len === slv_ar_chans_i[0].len)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_size : assert final(mst_ar_chans_o[0].size === slv_ar_chans_i[0].size)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_qos : assert final(mst_ar_chans_o[0].qos === slv_ar_chans_i[0].qos)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
r_id : assert final(mst_r_chans_i[0].id[$bits(slv_r_chans_o[0].id)-1:0] === slv_r_chans_o[0].id)
else $fatal (1, "Something with the R channel ID stripping went wrong.");
r_data : assert final(mst_r_chans_i[0].data === slv_r_chans_o[0].data)
else $fatal (1, "Something with the R channel ID stripping went wrong.");
r_resp : assert final(mst_r_chans_i[0].resp === slv_r_chans_o[0].resp)
else $fatal (1, "Something with the R channel ID stripping went wrong.");
`endif
// pragma translate_on
endmodule
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Description: Automatically generated bootrom
//
// Generated by hardware/scripts/generate_bootrom.py
module bootrom #(
/* Automatically generated. DO NOT CHANGE! */
parameter int unsigned DataWidth = 128,
parameter int unsigned AddrWidth = 32
) (
input logic clk_i,
input logic req_i,
input logic [AddrWidth-1:0] addr_i,
output logic [DataWidth-1:0] rdata_o
);
localparam int RomSize = 1;
localparam int AddrBits = RomSize > 1 ? $clog2(RomSize) : 1;
const logic [RomSize-1:0][DataWidth-1:0] mem = {
128'h00050067_10500073_00050513_e0000517
};
logic [AddrBits-1:0] addr_q;
always_ff @(posedge clk_i) begin
if (req_i) begin
addr_q <= addr_i[AddrBits-1+4:4];
end
end
// this prevents spurious Xes from propagating into
// the speculative fetch stage of the core
assign rdata_o = (addr_q < RomSize) ? mem[addr_q] : '0;
endmodule
// Copyright 2016 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.
/// cf_math_pkg: Constant Function Implementations of Mathematical Functions for HDL Elaboration
///
/// This package contains a collection of mathematical functions that are commonly used when defining
/// the value of constants in HDL code. These functions are implemented as Verilog constants
/// functions. Introduced in Verilog 2001 (IEEE Std 1364-2001), a constant function (§ 10.3.5) is a
/// function whose value can be evaluated at compile time or during elaboration. A constant function
/// must be called with arguments that are constants.
package cf_math_pkg;
/// Ceiled Division of Two Natural Numbers
///
/// Returns the quotient of two natural numbers, rounded towards plus infinity.
function automatic integer ceil_div (input longint dividend, input longint divisor);
automatic longint remainder;
// pragma translate_off
`ifndef VERILATOR
if (dividend < 0) begin
$fatal(1, "Dividend %0d is not a natural number!", dividend);
end
if (divisor < 0) begin
$fatal(1, "Divisor %0d is not a natural number!", divisor);
end
if (divisor == 0) begin
$fatal(1, "Division by zero!");
end
`endif
// pragma translate_on
remainder = dividend;
for (ceil_div = 0; remainder > 0; ceil_div++) begin
remainder = remainder - divisor;
end
endfunction
/// Index width required to be able to represent up to `num_idx` indices as a binary
/// encoded signal.
/// Ensures that the minimum width if an index signal is `1`, regardless of parametrization.
///
/// Sample usage in type definition:
/// As parameter:
/// `parameter type idx_t = logic[cf_math_pkg::idx_width(NumIdx)-1:0]`
/// As typedef:
/// `typedef logic [cf_math_pkg::idx_width(NumIdx)-1:0] idx_t`
function automatic integer unsigned idx_width (input integer unsigned num_idx);
return (num_idx > 32'd1) ? unsigned'($clog2(num_idx)) : 32'd1;
endfunction
endpackage
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
`include "common_cells/registers.svh"
module ctrl_registers #(
parameter int DataWidth = 32,
parameter int NumRegs = 0,
// Parameters
parameter logic [DataWidth-1:0] TCDMBaseAddr = 0,
parameter logic [DataWidth-1:0] TCDMSize = 0,
parameter logic [DataWidth-1:0] NumCores = 0,
// AXI Structs
parameter type axi_lite_req_t = logic,
parameter type axi_lite_resp_t = logic
) (
input logic clk_i,
input logic rst_ni,
// AXI Bus
input axi_lite_req_t axi_lite_slave_req_i,
output axi_lite_resp_t axi_lite_slave_resp_o,
// Control registers
output logic [DataWidth-1:0] eoc_o,
output logic eoc_valid_o,
output logic [NumCores-1:0] wake_up_o,
output logic [DataWidth-1:0] tcdm_start_address_o,
output logic [DataWidth-1:0] tcdm_end_address_o,
output logic [DataWidth-1:0] num_cores_o
);
import mempool_pkg::*;
/*****************
* Definitions *
*****************/
localparam int unsigned DataWidthInBytes = (DataWidth + 7) / 8;
localparam int unsigned RegNumBytes = NumRegs * DataWidthInBytes;
localparam int unsigned RegDataWidth = NumRegs * DataWidth;
localparam logic [DataWidthInBytes-1:0] ReadOnlyReg = {DataWidthInBytes{1'b1}};
localparam logic [DataWidthInBytes-1:0] ReadWriteReg = {DataWidthInBytes{1'b0}};
// Memory map
// [3:0]: eoc_reg (rw)
// [7:4]: wake_up_reg (rw)
// [11:8]: tcdm_start_adress_reg (ro)
// [15:12]:tcdm_end_address_reg (ro)
// [19:16]:nr_cores_address_reg (ro)
localparam logic [NumRegs-1:0][DataWidth-1:0] RegRstVal = '{
NumCores,
TCDMBaseAddr + TCDMSize,
TCDMBaseAddr,
{DataWidth{1'b0}},
{DataWidth{1'b0}}
};
localparam logic [NumRegs-1:0][DataWidthInBytes-1:0] AxiReadOnly = '{
ReadOnlyReg,
ReadOnlyReg,
ReadOnlyReg,
ReadWriteReg,
ReadWriteReg
};
/***************
* Registers *
***************/
logic [DataWidth-1:0] eoc;
logic [DataWidth-1:0] wake_up;
logic [DataWidth-1:0] tcdm_start_address;
logic [DataWidth-1:0] tcdm_end_address;
logic [DataWidth-1:0] num_cores;
logic [RegNumBytes-1:0] wr_active_d;
logic [RegNumBytes-1:0] wr_active_q;
axi_lite_regs #(
.RegNumBytes (RegNumBytes ),
.AxiAddrWidth(AddrWidth ),
.AxiDataWidth(AxiLiteDataWidth ),
.AxiReadOnly (AxiReadOnly ),
.RegRstVal (RegRstVal ),
.req_lite_t (axi_lite_req_t ),
.resp_lite_t (axi_lite_resp_t )
) i_axi_lite_regs (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.axi_req_i (axi_lite_slave_req_i ),
.axi_resp_o (axi_lite_slave_resp_o ),
.wr_active_o(wr_active_d ),
.rd_active_o(/* Unused */ ),
.reg_d_i ('0 ),
.reg_load_i ('0 ),
.reg_q_o ({num_cores, tcdm_end_address, tcdm_start_address, wake_up, eoc})
);
/***************
* Signals *
***************/
assign eoc_o = eoc >> 1;
assign tcdm_start_address_o = tcdm_start_address;
assign tcdm_end_address_o = tcdm_end_address;
assign num_cores_o = num_cores;
// converts 32 bit wake up to 256 bit
always_comb begin
wake_up_o = '0;
if (wr_active_q[7:4]) begin
if (wake_up < NumCores) begin
wake_up_o = 1 << wake_up;
end else if (wake_up == {DataWidth{1'b1}}) begin
wake_up_o = {NumCores{1'b1}};
end
end
end
assign eoc_valid_o = eoc[0];
// register to add +1 latency to the wr_active signal
`FF(wr_active_q, wr_active_d, '0, clk_i, rst_ni)
endmodule : ctrl_registers
// 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.
// Fall-through register with a simple stream-like ready/valid handshake.
// This register does not cut combinatorial paths on any signals: in case the module at its output
// is ready to accept data within the same clock cycle, they are forwarded. Use this module to get a
// 'default ready' behavior towards the input.
module fall_through_register #(
parameter type T = logic // Vivado requires a default value for type parameters.
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous active-low reset
input logic clr_i, // Synchronous clear
input logic testmode_i, // Test mode to bypass clock gating
// Input port
input logic valid_i,
output logic ready_o,
input T data_i,
// Output port
output logic valid_o,
input logic ready_i,
output T data_o
);
logic fifo_empty,
fifo_full;
fifo_v2 #(
.FALL_THROUGH (1'b1),
.DATA_WIDTH ($size(T)),
.DEPTH (1),
.dtype (T)
) i_fifo (
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (clr_i),
.testmode_i (testmode_i),
.full_o (fifo_full),
.empty_o (fifo_empty),
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i (data_i),
.push_i (valid_i & ~fifo_full),
.data_o (data_o),
.pop_i (ready_i & ~fifo_empty)
);
assign ready_o = ~fifo_full;
assign valid_o = ~fifo_empty;
endmodule
// 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>
module fifo_v2 #(
parameter bit FALL_THROUGH = 1'b0, // fifo is in fall-through mode
parameter int unsigned DATA_WIDTH = 32, // default data width if the fifo is of type logic
parameter int unsigned DEPTH = 8, // depth can be arbitrary from 0 to 2**32
parameter int unsigned ALM_EMPTY_TH = 1, // almost empty threshold (when to assert alm_empty_o)
parameter int unsigned ALM_FULL_TH = 1, // almost full threshold (when to assert alm_full_o)
parameter type dtype = logic [DATA_WIDTH-1:0],
// DO NOT OVERWRITE THIS PARAMETER
parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1
)(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic flush_i, // flush the queue
input logic testmode_i, // test_mode to bypass clock gating
// status flags
output logic full_o, // queue is full
output logic empty_o, // queue is empty
output logic alm_full_o, // FIFO fillstate >= the specified threshold
output logic alm_empty_o, // FIFO fillstate <= the specified threshold
// as long as the queue is not full we can push new data
input dtype data_i, // data to push into the queue
input logic push_i, // data is valid and can be pushed to the queue
// as long as the queue is not empty we can pop new elements
output dtype data_o, // output data
input logic pop_i // pop head from queue
);
logic [ADDR_DEPTH-1:0] usage;
// generate threshold parameters
if (DEPTH == 0) begin
assign alm_full_o = 1'b0; // that signal does not make any sense in a FIFO of depth 0
assign alm_empty_o = 1'b0; // that signal does not make any sense in a FIFO of depth 0
end else begin
assign alm_full_o = (usage >= ALM_FULL_TH[ADDR_DEPTH-1:0]);
assign alm_empty_o = (usage <= ALM_EMPTY_TH[ADDR_DEPTH-1:0]);
end
fifo_v3 #(
.FALL_THROUGH ( FALL_THROUGH ),
.DATA_WIDTH ( DATA_WIDTH ),
.DEPTH ( DEPTH ),
.dtype ( dtype )
) i_fifo_v3 (
.clk_i,
.rst_ni,
.flush_i,
.testmode_i,
.full_o,
.empty_o,
.usage_o (usage),
.data_i,
.push_i,
.data_o,
.pop_i
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (ALM_FULL_TH <= DEPTH) else $error("ALM_FULL_TH can't be larger than the DEPTH.");
assert (ALM_EMPTY_TH <= DEPTH) else $error("ALM_EMPTY_TH can't be larger than the DEPTH.");
end
`endif
// pragma translate_on
endmodule // fifo_v2
// 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>
module fifo_v3 #(
parameter bit FALL_THROUGH = 1'b0, // fifo is in fall-through mode
parameter int unsigned DATA_WIDTH = 32, // default data width if the fifo is of type logic
parameter int unsigned DEPTH = 8, // depth can be arbitrary from 0 to 2**32
parameter type dtype = logic [DATA_WIDTH-1:0],
// DO NOT OVERWRITE THIS PARAMETER
parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1
)(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic flush_i, // flush the queue
input logic testmode_i, // test_mode to bypass clock gating
// status flags
output logic full_o, // queue is full
output logic empty_o, // queue is empty
output logic [ADDR_DEPTH-1:0] usage_o, // fill pointer
// as long as the queue is not full we can push new data
input dtype data_i, // data to push into the queue
input logic push_i, // data is valid and can be pushed to the queue
// as long as the queue is not empty we can pop new elements
output dtype data_o, // output data
input logic pop_i // pop head from queue
);
// local parameter
// FIFO depth - handle the case of pass-through, synthesizer will do constant propagation
localparam int unsigned FifoDepth = (DEPTH > 0) ? DEPTH : 1;
// clock gating control
logic gate_clock;
// pointer to the read and write section of the queue
logic [ADDR_DEPTH - 1:0] read_pointer_n, read_pointer_q, write_pointer_n, write_pointer_q;
// keep a counter to keep track of the current queue status
// this integer will be truncated by the synthesis tool
logic [ADDR_DEPTH:0] status_cnt_n, status_cnt_q;
// actual memory
dtype [FifoDepth - 1:0] mem_n, mem_q;
assign usage_o = status_cnt_q[ADDR_DEPTH-1:0];
if (DEPTH == 0) begin : gen_pass_through
assign empty_o = ~push_i;
assign full_o = ~pop_i;
end else begin : gen_fifo
assign full_o = (status_cnt_q == FifoDepth[ADDR_DEPTH:0]);
assign empty_o = (status_cnt_q == 0) & ~(FALL_THROUGH & push_i);
end
// status flags
// read and write queue logic
always_comb begin : read_write_comb
// default assignment
read_pointer_n = read_pointer_q;
write_pointer_n = write_pointer_q;
status_cnt_n = status_cnt_q;
data_o = (DEPTH == 0) ? data_i : mem_q[read_pointer_q];
mem_n = mem_q;
gate_clock = 1'b1;
// push a new element to the queue
if (push_i && ~full_o) begin
// push the data onto the queue
mem_n[write_pointer_q] = data_i;
// un-gate the clock, we want to write something
gate_clock = 1'b0;
// increment the write counter
if (write_pointer_q == FifoDepth[ADDR_DEPTH-1:0] - 1)
write_pointer_n = '0;
else
write_pointer_n = write_pointer_q + 1;
// increment the overall counter
status_cnt_n = status_cnt_q + 1;
end
if (pop_i && ~empty_o) begin
// read from the queue is a default assignment
// but increment the read pointer...
if (read_pointer_n == FifoDepth[ADDR_DEPTH-1:0] - 1)
read_pointer_n = '0;
else
read_pointer_n = read_pointer_q + 1;
// ... and decrement the overall count
status_cnt_n = status_cnt_q - 1;
end
// keep the count pointer stable if we push and pop at the same time
if (push_i && pop_i && ~full_o && ~empty_o)
status_cnt_n = status_cnt_q;
// FIFO is in pass through mode -> do not change the pointers
if (FALL_THROUGH && (status_cnt_q == 0) && push_i) begin
data_o = data_i;
if (pop_i) begin
status_cnt_n = status_cnt_q;
read_pointer_n = read_pointer_q;
write_pointer_n = write_pointer_q;
end
end
end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
read_pointer_q <= '0;
write_pointer_q <= '0;
status_cnt_q <= '0;
end else begin
if (flush_i) begin
read_pointer_q <= '0;
write_pointer_q <= '0;
status_cnt_q <= '0;
end else begin
read_pointer_q <= read_pointer_n;
write_pointer_q <= write_pointer_n;
status_cnt_q <= status_cnt_n;
end
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
mem_q <= '0;
end else if (!gate_clock) begin
mem_q <= mem_n;
end
end
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (DEPTH > 0) else $error("DEPTH must be greater than 0.");
end
full_write : assert property(
@(posedge clk_i) disable iff (~rst_ni) (full_o |-> ~push_i))
else $fatal (1, "Trying to push new data although the FIFO is full.");
empty_read : assert property(
@(posedge clk_i) disable iff (~rst_ni) (empty_o |-> ~pop_i))
else $fatal (1, "Trying to pop data although the FIFO is empty.");
`endif
// pragma translate_on
endmodule // fifo_v3
// 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.
// Deprecated, use lzc unit instead.
/// A leading-one finder / leading zero counter.
/// Set FLIP to 0 for find_first_one => first_one_o is the index of the first one (from the LSB)
/// Set FLIP to 1 for leading zero counter => first_one_o is the number of leading zeroes (from the MSB)
module find_first_one #(
/// The width of the input vector.
parameter int WIDTH = -1,
parameter int FLIP = 0
)(
input logic [WIDTH-1:0] in_i,
output logic [$clog2(WIDTH)-1:0] first_one_o,
output logic no_ones_o
);
localparam int NUM_LEVELS = $clog2(WIDTH);
// pragma translate_off
initial begin
assert(WIDTH >= 0);
end
// pragma translate_on
logic [WIDTH-1:0][NUM_LEVELS-1:0] index_lut;
logic [2**NUM_LEVELS-1:0] sel_nodes;
logic [2**NUM_LEVELS-1:0][NUM_LEVELS-1:0] index_nodes;
logic [WIDTH-1:0] in_tmp;
for (genvar i = 0; i < WIDTH; i++) begin
assign in_tmp[i] = FLIP ? in_i[WIDTH-1-i] : in_i[i];
end
for (genvar j = 0; j < WIDTH; j++) begin
assign index_lut[j] = j;
end
for (genvar level = 0; level < NUM_LEVELS; level++) begin
if (level < NUM_LEVELS-1) begin
for (genvar l = 0; l < 2**level; l++) begin
assign sel_nodes[2**level-1+l] = sel_nodes[2**(level+1)-1+l*2] | sel_nodes[2**(level+1)-1+l*2+1];
assign index_nodes[2**level-1+l] = (sel_nodes[2**(level+1)-1+l*2] == 1'b1) ?
index_nodes[2**(level+1)-1+l*2] : index_nodes[2**(level+1)-1+l*2+1];
end
end
if (level == NUM_LEVELS-1) begin
for (genvar k = 0; k < 2**level; k++) begin
// if two successive indices are still in the vector...
if (k * 2 < WIDTH-1) begin
assign sel_nodes[2**level-1+k] = in_tmp[k*2] | in_tmp[k*2+1];
assign index_nodes[2**level-1+k] = (in_tmp[k*2] == 1'b1) ? index_lut[k*2] : index_lut[k*2+1];
end
// if only the first index is still in the vector...
if (k * 2 == WIDTH-1) begin
assign sel_nodes[2**level-1+k] = in_tmp[k*2];
assign index_nodes[2**level-1+k] = index_lut[k*2];
end
// if index is out of range
if (k * 2 > WIDTH-1) begin
assign sel_nodes[2**level-1+k] = 1'b0;
assign index_nodes[2**level-1+k] = '0;
end
end
end
end
assign first_one_o = NUM_LEVELS > 0 ? index_nodes[0] : '0;
assign no_ones_o = NUM_LEVELS > 0 ? ~sel_nodes[0] : '1;
endmodule
// Copyright 2020 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.
//
// Florian Zaruba <zarubaf@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
/// A register with handshakes that completely cuts any combinatorial paths
/// between the input and output in isochronous clock domains.
///
/// > Definition of isochronous: In telecommunication, an isochronous signal is a signal
/// > in which the time interval separating any two significant instants is equal to the
/// > unit interval or a multiple of the unit interval.
///
/// The source and destination clock domains must be derived from the same clock
/// but can vary in frequency by a constant factor (e.g., double the frequency).
///
/// The module is basically a two deep dual-clock fifo with read and write pointers
/// in different clock domains. As we know the static timing relationship between the
/// clock domains we can rely on static timing analysis (STA) to get the sampling windows
/// right and therefore don't need any synchronization.
///
/// # Restrictions
///
/// Source and destination clock domains must be an integer multiple of each other and
/// all timing-paths need to be covered by STA. For example a recommended SDC would be:
///
/// `create_generated_clock dst_clk_i -name dst_clk -source src_clk_i -divide_by 2
///
/// There are _no_ restrictions on which clock domain should be the faster, any integer
/// ratio will work.
module isochronous_spill_register #(
/// Data type of spill register.
parameter type T = logic,
/// Make this spill register transparent.
parameter bit Bypass = 1'b0
) (
/// Clock of source clock domain.
input logic src_clk_i,
/// Active low async reset in source domain.
input logic src_rst_ni,
/// Source input data is valid.
input logic src_valid_i,
/// Source is ready to accept.
output logic src_ready_o,
/// Source input data.
input T src_data_i,
/// Clock of destination clock domain.
input logic dst_clk_i,
/// Active low async reset in destination domain.
input logic dst_rst_ni,
/// Destination output data is valid.
output logic dst_valid_o,
/// Destination is ready to accept.
input logic dst_ready_i,
/// Destination output data.
output T dst_data_o
);
// Don't generate the spill register.
if (Bypass) begin : gen_bypass
assign dst_valid_o = src_valid_i;
assign src_ready_o = dst_ready_i;
assign dst_data_o = src_data_i;
// Generate the spill register
end else begin : gen_isochronous_spill_register
/// Read/write pointer are one bit wider than necessary.
/// We implicitly capture the full and empty state with the second bit:
/// If all but the topmost bit of `rd_pointer_q` and `wr_pointer_q` agree, the
/// FIFO is in a critical state. If the topmost bit is equal, the FIFO is
/// empty, otherwise it is full.
logic [1:0] rd_pointer_q, wr_pointer_q;
// Advance write pointer if we pushed a new item into the FIFO. (Source clock domain)
`FFLARN(wr_pointer_q, wr_pointer_q+1, (src_valid_i && src_ready_o), '0, src_clk_i, src_rst_ni)
// Advance read pointer if downstream consumed an item. (Destination clock domain)
`FFLARN(rd_pointer_q, rd_pointer_q+1, (dst_valid_o && dst_ready_i), '0, dst_clk_i, dst_rst_ni)
T [1:0] mem_d, mem_q;
`FFLNR(mem_q, mem_d, (src_valid_i && src_ready_o), src_clk_i)
always_comb begin
mem_d = mem_q;
mem_d[wr_pointer_q[0]] = src_data_i;
end
assign src_ready_o = (rd_pointer_q ^ wr_pointer_q) != 2'b10;
assign dst_valid_o = (rd_pointer_q ^ wr_pointer_q) != '0;
assign dst_data_o = mem_q[rd_pointer_q[0]];
end
// pragma translate_off
// stability guarantees
`ifndef VERILATOR
assert property (@(posedge src_clk_i) disable iff (src_rst_ni)
(src_valid_i && !src_ready_o |=> $stable(src_valid_i))) else $error("src_valid_i is unstable");
assert property (@(posedge src_clk_i) disable iff (src_rst_ni)
(src_valid_i && !src_ready_o |=> $stable(src_data_i))) else $error("src_data_i is unstable");
assert property (@(posedge dst_clk_i) disable iff (dst_rst_ni)
(dst_valid_o && !dst_ready_i |=> $stable(dst_valid_o))) else $error("dst_valid_o is unstable");
assert property (@(posedge dst_clk_i) disable iff (dst_rst_ni)
(dst_valid_o && !dst_ready_i |=> $stable(dst_data_o))) else $error("dst_data_o is unstable");
`endif
// pragma translate_on
endmodule
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
module latch_scm #(
parameter ADDR_WIDTH = 5,
parameter DATA_WIDTH = 32
) (
input logic clk,
// Read port
input logic ReadEnable,
input logic [ADDR_WIDTH-1:0] ReadAddr,
output logic [DATA_WIDTH-1:0] ReadData,
// Write port
input logic WriteEnable,
input logic [ADDR_WIDTH-1:0] WriteAddr,
input logic [DATA_WIDTH-1:0] WriteData
);
localparam NUM_WORDS = 2**ADDR_WIDTH;
// Read address register, located at the input of the address decoder
logic [ADDR_WIDTH-1:0] RAddrRegxDP;
logic [NUM_WORDS-1:0] RAddrOneHotxD;
logic [DATA_WIDTH-1:0] MemContentxDP[NUM_WORDS];
logic [NUM_WORDS-1:0] WAddrOneHotxD;
logic [NUM_WORDS-1:0] ClocksxC;
logic [DATA_WIDTH-1:0] WDataIntxD;
logic clk_int;
int unsigned i;
int unsigned j;
int unsigned k;
int unsigned l;
int unsigned m;
genvar x;
genvar y;
tc_clk_gating CG_WE_GLOBAL (
.clk_o (clk_int ),
.en_i (WriteEnable),
.test_en_i (1'b0 ),
.clk_i (clk )
);
//-----------------------------------------------------------------------------
//-- READ : Read address register
//-----------------------------------------------------------------------------
always_ff @(posedge clk) begin : p_RAddrReg
if(ReadEnable)
RAddrRegxDP <= ReadAddr;
end
//-----------------------------------------------------------------------------
//-- READ : Read address decoder RAD
//-----------------------------------------------------------------------------
always_comb begin : p_RAD
RAddrOneHotxD = '0;
RAddrOneHotxD[RAddrRegxDP] = 1'b1;
end
assign ReadData = MemContentxDP[RAddrRegxDP];
//-----------------------------------------------------------------------------
//-- WRITE : Write Address Decoder (WAD), combinatorial process
//-----------------------------------------------------------------------------
always_comb begin : p_WAD
for(i=0; i<NUM_WORDS; i++) begin : p_WordIter
if ( (WriteEnable == 1'b1 ) && (WriteAddr == i) )
WAddrOneHotxD[i] = 1'b1;
else
WAddrOneHotxD[i] = 1'b0;
end
end
//-----------------------------------------------------------------------------
//-- WRITE : Clock gating (if integrated clock-gating cells are available)
//-----------------------------------------------------------------------------
generate
for(x=0; x<NUM_WORDS; x++) begin : CG_CELL_WORD_ITER
tc_clk_gating CG_Inst (
.clk_o ( ClocksxC[x] ),
.en_i ( WAddrOneHotxD[x] ),
.test_en_i ( 1'b0 ),
.clk_i ( clk_int )
);
end
endgenerate
//-----------------------------------------------------------------------------
// WRITE : SAMPLE INPUT DATA
//---------------------------------------------------------------------------
always_ff @(posedge clk) begin : sample_waddr
if(WriteEnable)
WDataIntxD <= WriteData;
end
//-----------------------------------------------------------------------------
//-- WRITE : Write operation
//-----------------------------------------------------------------------------
//-- Generate M = WORDS sequential processes, each of which describes one
//-- word of the memory. The processes are synchronized with the clocks
//-- ClocksxC(i), i = 0, 1, ..., M-1
//-- Use active low, i.e. transparent on low latches as storage elements
//-- Data is sampled on rising clock edge
/* verilator lint_off NOLATCH */
always_latch begin : latch_wdata
for(k=0; k<NUM_WORDS; k++) begin : w_WordIter
if( ClocksxC[k] == 1'b1)
MemContentxDP[k] <= WDataIntxD;
end
end
/* verilator lint_on NOLATCH */
endmodule
// Copyright (c) 2018 - 2019 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.
/// A trailing zero counter / leading zero counter.
/// Set MODE to 0 for trailing zero counter => cnt_o is the number of trailing zeros (from the LSB)
/// Set MODE to 1 for leading zero counter => cnt_o is the number of leading zeros (from the MSB)
/// If the input does not contain a zero, `empty_o` is asserted. Additionally `cnt_o` contains
/// the maximum number of zeros - 1. For example:
/// in_i = 000_0000, empty_o = 1, cnt_o = 6 (mode = 0)
/// in_i = 000_0001, empty_o = 0, cnt_o = 0 (mode = 0)
/// in_i = 000_1000, empty_o = 0, cnt_o = 3 (mode = 0)
/// Furthermore, this unit contains a more efficient implementation for Verilator (simulation only).
/// This speeds up simulation significantly.
module lzc #(
/// The width of the input vector.
parameter int unsigned WIDTH = 2,
/// Mode selection: 0 -> trailing zero, 1 -> leading zero
parameter bit MODE = 1'b0,
/// Dependent parameter. Do **not** change!
///
/// Width of the output signal with the zero count.
parameter int unsigned CNT_WIDTH = cf_math_pkg::idx_width(WIDTH)
) (
/// Input vector to be counted.
input logic [WIDTH-1:0] in_i,
/// Count of the leading / trailing zeros.
output logic [CNT_WIDTH-1:0] cnt_o,
/// Counter is empty: Asserted if all bits in in_i are zero.
output logic empty_o
);
if (WIDTH == 1) begin : gen_degenerate_lzc
assign cnt_o[0] = !in_i[0];
assign empty_o = !in_i[0];
end else begin : gen_lzc
localparam int unsigned NumLevels = $clog2(WIDTH);
// pragma translate_off
initial begin
assert(WIDTH > 0) else $fatal(1, "input must be at least one bit wide");
end
// pragma translate_on
logic [WIDTH-1:0][NumLevels-1:0] index_lut;
logic [2**NumLevels-1:0] sel_nodes;
logic [2**NumLevels-1:0][NumLevels-1:0] index_nodes;
logic [WIDTH-1:0] in_tmp;
// reverse vector if required
always_comb begin : flip_vector
for (int unsigned i = 0; i < WIDTH; i++) begin
in_tmp[i] = (MODE) ? in_i[WIDTH-1-i] : in_i[i];
end
end
for (genvar j = 0; unsigned'(j) < WIDTH; j++) begin : g_index_lut
assign index_lut[j] = (NumLevels)'(unsigned'(j));
end
for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : g_levels
if (unsigned'(level) == NumLevels - 1) begin : g_last_level
for (genvar k = 0; k < 2 ** level; k++) begin : g_level
// if two successive indices are still in the vector...
if (unsigned'(k) * 2 < WIDTH - 1) begin : g_reduce
assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2] | in_tmp[k * 2 + 1];
assign index_nodes[2 ** level - 1 + k] = (in_tmp[k * 2] == 1'b1)
? index_lut[k * 2] :
index_lut[k * 2 + 1];
end
// if only the first index is still in the vector...
if (unsigned'(k) * 2 == WIDTH - 1) begin : g_base
assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2];
assign index_nodes[2 ** level - 1 + k] = index_lut[k * 2];
end
// if index is out of range
if (unsigned'(k) * 2 > WIDTH - 1) begin : g_out_of_range
assign sel_nodes[2 ** level - 1 + k] = 1'b0;
assign index_nodes[2 ** level - 1 + k] = '0;
end
end
end else begin : g_not_last_level
for (genvar l = 0; l < 2 ** level; l++) begin : g_level
assign sel_nodes[2 ** level - 1 + l] =
sel_nodes[2 ** (level + 1) - 1 + l * 2] | sel_nodes[2 ** (level + 1) - 1 + l * 2 + 1];
assign index_nodes[2 ** level - 1 + l] = (sel_nodes[2 ** (level + 1) - 1 + l * 2] == 1'b1)
? index_nodes[2 ** (level + 1) - 1 + l * 2] :
index_nodes[2 ** (level + 1) - 1 + l * 2 + 1];
end
end
end
assign cnt_o = NumLevels > unsigned'(0) ? index_nodes[0] : {($clog2(WIDTH)) {1'b0}};
assign empty_o = NumLevels > unsigned'(0) ? ~sel_nodes[0] : ~(|in_i);
end : gen_lzc
endmodule : lzc
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
module mempool_cluster_wrap
import mempool_pkg::*;
#(
// TCDM
parameter addr_t TCDMBaseAddr = 32'b0000_0000,
// Boot address
parameter logic [31:0] BootAddr = 32'h0000_0000,
// Dependant parameters. DO NOT CHANGE!
parameter int unsigned NumAXIMasters = NumGroups
) (
// Clock and reset
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
// Scan chain
input logic scan_enable_i,
input logic scan_data_i,
output logic scan_data_o,
// Wake up signal
input logic [NumCores-1:0] wake_up_i,
// AXI Interface
output axi_tile_req_t [NumAXIMasters-1:0] axi_mst_req_o,
input axi_tile_resp_t [NumAXIMasters-1:0] axi_mst_resp_i
);
/*********************
* MemPool Cluster *
*********************/
mempool_cluster #(
.TCDMBaseAddr(TCDMBaseAddr),
.BootAddr (BootAddr )
) i_mempool_cluster (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.testmode_i (testmode_i ),
.scan_enable_i (scan_enable_i ),
.scan_data_i (scan_data_i ),
.scan_data_o (scan_data_o ),
.wake_up_i ('0 ),
.axi_mst_req_o (axi_mst_req_o ),
.axi_mst_resp_i(axi_mst_resp_i)
);
endmodule : mempool_cluster_wrap
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// csamudra : 12/24 edited line 18 localparam integer ...NumCores ..16..endif; earlier it was 0 instead of 16
// csamudra : 12/24 edited line 19 localparam integer ...NumCoresPerTile..4..endif; earlier it was 0 instead of 4
package mempool_pkg;
import snitch_pkg::MetaIdWidth;
import cf_math_pkg::idx_width;
/*********************
* TILE PARAMETERS *
*********************/
`include "axi/assign.svh"
`include "axi/typedef.svh"
localparam integer unsigned NumCores = `ifdef NUM_CORES `NUM_CORES `else 16 `endif;
localparam integer unsigned NumCoresPerTile = `ifdef NUM_CORES_PER_TILE `NUM_CORES_PER_TILE `else 4 `endif;
localparam integer unsigned NumGroups = 4;
localparam integer unsigned NumTiles = NumCores / NumCoresPerTile;
localparam integer unsigned NumTilesPerGroup = NumTiles / NumGroups;
localparam integer unsigned NumCoresPerGroup = NumCores / NumGroups;
localparam integer unsigned NumCoresPerCache = NumCoresPerTile;
localparam integer unsigned AxiCoreIdWidth = 1;
localparam integer unsigned AxiTileIdWidth = AxiCoreIdWidth+1; // + 1 for cache
localparam integer unsigned AxiDataWidth = 128;
localparam integer unsigned AxiLiteDataWidth = 32;
/***********************
* MEMORY PARAMETERS *
***********************/
localparam integer unsigned AddrWidth = 32;
localparam integer unsigned DataWidth = 32;
localparam integer unsigned BeWidth = DataWidth / 8;
localparam integer unsigned ByteOffset = $clog2(BeWidth);
localparam integer unsigned BankingFactor = 4;
localparam bit LrScEnable = 1'b1;
localparam integer unsigned TCDMSizePerBank = 1024; // [B]
localparam integer unsigned NumBanks = NumCores * BankingFactor;
localparam integer unsigned NumBanksPerTile = NumBanks / NumTiles;
localparam integer unsigned NumBanksPerGroup = NumBanks / NumGroups;
localparam integer unsigned TCDMAddrMemWidth = $clog2(TCDMSizePerBank / mempool_pkg::BeWidth);
localparam integer unsigned TCDMAddrWidth = TCDMAddrMemWidth + idx_width(NumBanksPerGroup);
localparam integer unsigned L2Size = `ifdef L2_SIZE `L2_SIZE `else 0 `endif; // [B]
localparam integer unsigned L2BeWidth = AxiDataWidth/8;
localparam integer unsigned L2ByteOffset = $clog2(L2BeWidth);
typedef logic [AxiCoreIdWidth-1:0] axi_core_id_t;
typedef logic [AxiTileIdWidth-1:0] axi_tile_id_t;
typedef logic [AxiDataWidth-1:0] axi_data_t;
typedef logic [AxiDataWidth/8-1:0] axi_strb_t;
typedef logic [AxiLiteDataWidth-1:0] axi_lite_data_t;
typedef logic [AxiLiteDataWidth/8-1:0] axi_lite_strb_t;
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [BeWidth-1:0] strb_t;
localparam NumSystemXbarMasters = NumGroups + 1;
localparam AxiSystemIdWidth = $clog2(NumSystemXbarMasters) + AxiTileIdWidth;
typedef logic [AxiSystemIdWidth-1:0] axi_system_id_t;
localparam NumTestbenchXbarMasters = 1;
localparam AxiTestbenchIdWidth = $clog2(NumTestbenchXbarMasters) + AxiSystemIdWidth;
typedef logic [AxiTestbenchIdWidth-1:0] axi_tb_id_t;
`AXI_TYPEDEF_AW_CHAN_T(axi_core_aw_t, addr_t, axi_core_id_t, logic);
`AXI_TYPEDEF_W_CHAN_T(axi_core_w_t, axi_data_t, axi_strb_t, logic);
`AXI_TYPEDEF_B_CHAN_T(axi_core_b_t, axi_core_id_t, logic);
`AXI_TYPEDEF_AR_CHAN_T(axi_core_ar_t, addr_t, axi_core_id_t, logic);
`AXI_TYPEDEF_R_CHAN_T(axi_core_r_t, axi_data_t, axi_core_id_t, logic);
`AXI_TYPEDEF_REQ_T(axi_core_req_t, axi_core_aw_t, axi_core_w_t, axi_core_ar_t);
`AXI_TYPEDEF_RESP_T(axi_core_resp_t, axi_core_b_t, axi_core_r_t );
`AXI_TYPEDEF_AW_CHAN_T(axi_tile_aw_t, addr_t, axi_tile_id_t, logic);
`AXI_TYPEDEF_W_CHAN_T(axi_tile_w_t, axi_data_t, axi_strb_t, logic);
`AXI_TYPEDEF_B_CHAN_T(axi_tile_b_t, axi_tile_id_t, logic);
`AXI_TYPEDEF_AR_CHAN_T(axi_tile_ar_t, addr_t, axi_tile_id_t, logic);
`AXI_TYPEDEF_R_CHAN_T(axi_tile_r_t, axi_data_t, axi_tile_id_t, logic);
`AXI_TYPEDEF_REQ_T(axi_tile_req_t, axi_tile_aw_t, axi_tile_w_t, axi_tile_ar_t);
`AXI_TYPEDEF_RESP_T(axi_tile_resp_t, axi_tile_b_t, axi_tile_r_t );
`AXI_TYPEDEF_AW_CHAN_T(axi_system_aw_t, addr_t, axi_system_id_t, logic);
`AXI_TYPEDEF_W_CHAN_T(axi_system_w_t, axi_data_t, axi_strb_t, logic);
`AXI_TYPEDEF_B_CHAN_T(axi_system_b_t, axi_system_id_t, logic);
`AXI_TYPEDEF_AR_CHAN_T(axi_system_ar_t, addr_t, axi_system_id_t, logic);
`AXI_TYPEDEF_R_CHAN_T(axi_system_r_t, axi_data_t, axi_system_id_t, logic);
`AXI_TYPEDEF_REQ_T(axi_system_req_t, axi_system_aw_t, axi_system_w_t, axi_system_ar_t);
`AXI_TYPEDEF_RESP_T(axi_system_resp_t, axi_system_b_t, axi_system_r_t);
// AXI to ctrl registers
`AXI_TYPEDEF_W_CHAN_T(axi_ctrl_w_t, axi_lite_data_t, axi_lite_strb_t, logic);
`AXI_TYPEDEF_R_CHAN_T(axi_ctrl_r_t, axi_lite_data_t, axi_system_id_t, logic);
`AXI_TYPEDEF_REQ_T(axi_ctrl_req_t, axi_system_aw_t, axi_ctrl_w_t, axi_system_ar_t);
`AXI_TYPEDEF_RESP_T(axi_ctrl_resp_t, axi_system_b_t, axi_ctrl_r_t);
`AXI_TYPEDEF_AW_CHAN_T(axi_tb_aw_t, addr_t, axi_tb_id_t, logic);
`AXI_TYPEDEF_W_CHAN_T(axi_tb_w_t, axi_data_t, axi_strb_t, logic);
`AXI_TYPEDEF_B_CHAN_T(axi_tb_b_t, axi_tb_id_t, logic);
`AXI_TYPEDEF_AR_CHAN_T(axi_tb_ar_t, addr_t, axi_tb_id_t, logic);
`AXI_TYPEDEF_R_CHAN_T(axi_tb_r_t, axi_data_t, axi_tb_id_t, logic);
`AXI_TYPEDEF_REQ_T(axi_tb_req_t, axi_tb_aw_t, axi_tb_w_t, axi_tb_ar_t);
`AXI_TYPEDEF_RESP_T(axi_tb_resp_t, axi_tb_b_t, axi_tb_r_t);
`AXI_LITE_TYPEDEF_AW_CHAN_T(axi_lite_slv_aw_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(axi_lite_slv_w_t, axi_lite_data_t, axi_lite_strb_t)
`AXI_LITE_TYPEDEF_B_CHAN_T(axi_lite_slv_b_t)
`AXI_LITE_TYPEDEF_AR_CHAN_T(axi_lite_slv_ar_t, addr_t)
`AXI_LITE_TYPEDEF_R_CHAN_T(axi_lite_slv_r_t, axi_lite_data_t)
`AXI_LITE_TYPEDEF_REQ_T(axi_lite_slv_req_t, axi_lite_slv_aw_t, axi_lite_slv_w_t, axi_lite_slv_ar_t)
`AXI_LITE_TYPEDEF_RESP_T(axi_lite_slv_resp_t, axi_lite_slv_b_t, axi_lite_slv_r_t)
/***********************
* INSTRUCTION CACHE *
***********************/
localparam int unsigned ICacheSizeByte = 512 * NumCoresPerCache; // Total Size of instruction cache in bytes
localparam int unsigned ICacheSets = NumCoresPerCache / 2; // Number of sets
localparam int unsigned ICacheLineWidth = 32 * 2 * NumCoresPerCache; // Size of each cache line in bits,
/**********************************
* TCDM INTERCONNECT PARAMETERS *
**********************************/
typedef logic [TCDMAddrWidth-1:0] tcdm_addr_t;
typedef logic [TCDMAddrMemWidth-1:0] bank_addr_t;
typedef logic [TCDMAddrMemWidth+idx_width(NumBanksPerTile)-1:0] tile_addr_t;
typedef logic [MetaIdWidth-1:0] meta_id_t;
typedef logic [idx_width(NumCoresPerTile)-1:0] tile_core_id_t;
typedef logic [idx_width(NumTilesPerGroup)-1:0] tile_group_id_t;
typedef logic [idx_width(NumGroups)-1:0] group_id_t;
typedef logic [3:0] amo_t;
typedef struct packed {
meta_id_t meta_id;
tile_core_id_t core_id;
amo_t amo;
data_t data;
} tcdm_payload_t;
typedef struct packed {
tcdm_payload_t wdata;
logic wen;
strb_t be;
tcdm_addr_t tgt_addr;
} tcdm_master_req_t;
typedef struct packed {
tcdm_payload_t rdata;
} tcdm_master_resp_t;
typedef struct packed {
tcdm_payload_t wdata;
logic wen;
strb_t be;
tile_addr_t tgt_addr;
tile_group_id_t ini_addr;
} tcdm_slave_req_t;
typedef struct packed {
tcdm_payload_t rdata;
tile_group_id_t ini_addr;
} tcdm_slave_resp_t;
/*****************
* ADDRESS MAP *
*****************/
// Size in bytes of memory that is sequentially addressable per tile
localparam int unsigned SeqMemSizePerTile = NumCoresPerTile*1024; // 1 KiB
typedef struct packed {
int unsigned slave_idx;
addr_t mask;
addr_t value;
} address_map_t;
/***********************
* TRAFFIC GENERATOR *
***********************/
// Replaces core with a traffic generator
parameter bit TrafficGeneration = `ifdef TRAFFIC_GEN `TRAFFIC_GEN `else 0 `endif;
endpackage : mempool_pkg
// 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.
// Franceco Conti <fconti@iis.ee.ethz.ch>
module onehot_to_bin #(
parameter int unsigned ONEHOT_WIDTH = 16,
// Do Not Change
parameter int unsigned BIN_WIDTH = ONEHOT_WIDTH == 1 ? 1 : $clog2(ONEHOT_WIDTH)
) (
input logic [ONEHOT_WIDTH-1:0] onehot,
output logic [BIN_WIDTH-1:0] bin
);
for (genvar j = 0; j < BIN_WIDTH; j++) begin : jl
logic [ONEHOT_WIDTH-1:0] tmp_mask;
for (genvar i = 0; i < ONEHOT_WIDTH; i++) begin : il
logic [BIN_WIDTH-1:0] tmp_i;
assign tmp_i = i;
assign tmp_mask[i] = tmp_i[j];
end
assign bin[j] = |(tmp_mask & onehot);
end
// pragma translate_off
`ifndef VERILATOR
assert final ($onehot0(onehot)) else
$fatal(1, "[onehot_to_bin] More than two bit set in the one-hot signal");
`endif
// pragma translate_on
endmodule
This source diff could not be displayed because it is too large. You can view the blob instead.
// 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: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Demux based on address
module snitch_addr_demux
import mempool_pkg::address_map_t;
import cf_math_pkg::idx_width;
#(
parameter int unsigned NrOutput = 2 ,
parameter int unsigned AddressWidth = 32 ,
parameter int unsigned NumRules = 1 , // Routing rules
parameter type req_t = logic,
parameter type resp_t = logic,
/// Dependent parameters, DO NOT OVERRIDE!
localparam integer LogNrOutput = idx_width(NrOutput)
) (
input logic clk_i,
input logic rst_ni,
// request port
input logic [AddressWidth-1:0] req_addr_i,
input req_t req_payload_i,
input logic req_valid_i,
output logic req_ready_o,
output resp_t resp_payload_o,
output logic resp_valid_o,
input logic resp_ready_i,
// response port
output req_t [NrOutput-1:0] req_payload_o,
output logic [NrOutput-1:0] req_valid_o,
input logic [NrOutput-1:0] req_ready_i,
input resp_t [NrOutput-1:0] resp_payload_i,
input logic [NrOutput-1:0] resp_valid_i,
output logic [NrOutput-1:0] resp_ready_o,
input address_map_t [NumRules-1:0] address_map_i
);
logic [LogNrOutput-1:0] slave_select;
logic [NumRules-1:0] addr_match;
logic [idx_width(NumRules)-1:0] rule_select;
assign slave_select = address_map_i[rule_select].slave_idx;
// Address Decoder
always_comb begin : addr_decoder
for (int i = 0; i < NumRules; i++) begin
addr_match[i] = (req_addr_i & address_map_i[i].mask) == address_map_i[i].value;
end
end
find_first_one #(
.WIDTH(NumRules)
) find_slave_select (
.in_i ( addr_match ),
.first_one_o( rule_select ),
.no_ones_o ( /* Unused */ )
);
// Demux request to correct interconnect
stream_demux #(
.N_OUP ( NrOutput )
) i_req_demux (
.inp_valid_i ( req_valid_i ),
.inp_ready_o ( req_ready_o ),
.oup_sel_i ( slave_select ),
.oup_valid_o ( req_valid_o ),
.oup_ready_i ( req_ready_i )
);
for (genvar i = 0; i < NrOutput; i++) begin : gen_req_outputs
assign req_payload_o[i] = req_payload_i;
end
// Merge the response streams
logic [idx_width(NrOutput)-1:0] rr_prio;
assign rr_prio = 0;
rr_arb_tree #(
.DataType (resp_t ),
.NumIn (NrOutput),
.AxiVldRdy(1'b1 ),
.ExtPrio (1'b1 )
) i_resp_stream_arbiter (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i(1'b0 ),
.rr_i (rr_prio ),
.req_i (resp_valid_i ),
.data_i (resp_payload_i),
.gnt_o (resp_ready_o ),
.req_o (resp_valid_o ),
.data_o (resp_payload_o),
.gnt_i (resp_ready_i ),
.idx_o (/* Unused */ )
);
/* pragma translate_off */
`ifdef FORMAL
logic f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge clk_i)
f_past_valid <= 1'b1;
// assert reset in time step zero and deassert
assume property (@(posedge clk_i) !f_past_valid |-> !rst_ni);
// make sure that we get a response for each read we issued
for (genvar i = 0; i < NrOutput; i++) begin
assume property (@(posedge clk_i) disable iff (!rst_ni) (resp_valid_i[i] & resp_ready_o[i]) |-> $past(req_valid_o[i] & req_ready_i[i] & !req_write_i));
end
`endif
// check that we propagate a downstream request directly (e.g. combinatorial)
assert property (@(posedge clk_i) disable iff (!rst_ni) (req_valid_i & req_ready_o) |-> |(req_valid_o & req_ready_i));
/* pragma translate_on */
endmodule
// Copyright 2018-2019 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// File: axi_adapter.sv
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Date: 1.8.2018
//
// Description: Manages communication with the AXI Bus
module snitch_axi_adapter #(
parameter int unsigned WriteFIFODepth = 2,
parameter int unsigned ReadFIFODepth = 2,
parameter type addr_t = logic,
parameter type data_t = logic,
parameter type strb_t = logic,
parameter type axi_mst_req_t = logic,
parameter type axi_mst_resp_t = logic
) (
input logic clk_i,
input logic rst_ni,
// AXI port
input axi_mst_resp_t axi_resp_i,
output axi_mst_req_t axi_req_o,
input addr_t slv_qaddr_i,
input logic slv_qwrite_i,
input logic [3:0] slv_qamo_i,
input data_t slv_qdata_i,
input logic [2:0] slv_qsize_i,
input strb_t slv_qstrb_i,
input logic [7:0] slv_qrlen_i,
input logic slv_qvalid_i,
output logic slv_qready_o,
output data_t slv_pdata_o,
output logic slv_perror_o,
output logic slv_plast_o,
output logic slv_pvalid_o,
input logic slv_pready_i
);
localparam DataWidth = $bits(data_t);
localparam StrbWidth = $bits(strb_t);
localparam SlvByteOffset = $clog2($bits(strb_t));
localparam AxiByteOffset = $clog2($bits(axi_req_o.w.strb));
typedef enum logic [3:0] {
AMONone = 4'h0,
AMOSwap = 4'h1,
AMOAdd = 4'h2,
AMOAnd = 4'h3,
AMOOr = 4'h4,
AMOXor = 4'h5,
AMOMax = 4'h6,
AMOMaxu = 4'h7,
AMOMin = 4'h8,
AMOMinu = 4'h9,
AMOLR = 4'hA,
AMOSC = 4'hB
} amo_op_t;
typedef struct packed {
data_t data;
strb_t strb;
} write_t;
logic write_full;
logic write_empty;
logic read_full;
write_t write_data_in;
write_t write_data_out;
assign axi_req_o.aw.addr = slv_qaddr_i;
assign axi_req_o.aw.prot = 3'b0;
assign axi_req_o.aw.region = 4'b0;
assign axi_req_o.aw.size = slv_qsize_i;
assign axi_req_o.aw.len = '0;
assign axi_req_o.aw.burst = axi_pkg::BURST_INCR;
assign axi_req_o.aw.lock = 1'b0;
assign axi_req_o.aw.cache = axi_pkg::CACHE_MODIFIABLE;
assign axi_req_o.aw.qos = 4'b0;
assign axi_req_o.aw.id = '0;
assign axi_req_o.aw.user = '0;
assign axi_req_o.aw_valid = ~write_full & slv_qvalid_i & slv_qwrite_i;
always_comb begin
write_data_in.data = slv_qdata_i;
write_data_in.strb = slv_qstrb_i;
unique case (amo_op_t'(slv_qamo_i))
// RISC-V atops have a load semantic
AMOSwap: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ATOMICSWAP};
AMOAdd: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
AMOAnd: begin
// in this case we need to invert the data to get a "CLR"
write_data_in.data = ~slv_qdata_i;
axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
end
AMOOr: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
AMOXor: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
AMOMax: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
AMOMaxu: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
AMOMin: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
AMOMinu: axi_req_o.aw.atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
default: axi_req_o.aw.atop = '0;
endcase
end
localparam int unsigned ShiftWidth = (SlvByteOffset == AxiByteOffset) ? 1 : AxiByteOffset - SlvByteOffset;
typedef logic [ShiftWidth-1:0] shift_t;
typedef struct packed {
write_t data;
shift_t shift;
} write_ext_t;
if (SlvByteOffset == AxiByteOffset) begin : gen_w_data
// Write
fifo_v3 #(
.DEPTH ( WriteFIFODepth ),
.dtype ( write_t )
) i_fifo_w_data (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( write_full ),
.empty_o ( write_empty ),
.usage_o ( /* NC */ ),
.data_i ( write_data_in ),
.push_i ( slv_qvalid_i & slv_qready_o & slv_qwrite_i ),
.data_o ( write_data_out ),
.pop_i ( axi_req_o.w_valid & axi_resp_i.w_ready )
);
assign axi_req_o.w.data = write_data_out.data;
assign axi_req_o.w.strb = write_data_out.strb;
// Read
assign read_full = 1'b0;
assign slv_pdata_o = axi_resp_i.r.data;
end else begin : gen_w_data
// Write
write_ext_t write_data_ext_in, write_data_ext_out;
fifo_v3 #(
.DEPTH ( WriteFIFODepth ),
.dtype ( write_ext_t )
) i_fifo_w_data (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( write_full ),
.empty_o ( write_empty ),
.usage_o ( /* NC */ ),
.data_i ( write_data_ext_in ),
.push_i ( slv_qvalid_i & slv_qready_o & slv_qwrite_i ),
.data_o ( write_data_ext_out ),
.pop_i ( axi_req_o.w_valid & axi_resp_i.w_ready )
);
assign write_data_ext_in.data = write_data_in;
assign write_data_ext_in.shift = slv_qaddr_i[AxiByteOffset-1:SlvByteOffset];
assign axi_req_o.w.data = {'0, write_data_ext_out.data.data} << ($bits(data_t) * write_data_ext_out.shift);
assign axi_req_o.w.strb = {'0, write_data_ext_out.data.strb} << ($bits(strb_t) * write_data_ext_out.shift);
// Read
shift_t read_shift;
fifo_v3 #(
.DEPTH ( ReadFIFODepth ),
.DATA_WIDTH ( AxiByteOffset-SlvByteOffset )
) i_fifo_r_shift (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( read_full ),
.empty_o ( /* NC */ ),
.usage_o ( /* NC */ ),
.data_i ( slv_qaddr_i[AxiByteOffset-1:SlvByteOffset] ),
.push_i ( slv_qvalid_i & slv_qready_o & ~slv_qwrite_i ),
.data_o ( read_shift ),
.pop_i ( axi_resp_i.r_valid & slv_pready_i )
);
assign slv_pdata_o = axi_resp_i.r.data >> ($bits(data_t) * read_shift);
end
assign axi_req_o.w.last = 1'b1;
assign axi_req_o.w.user = '0;
assign axi_req_o.w_valid = ~write_empty;
assign axi_req_o.b_ready = 1'b1;
assign axi_req_o.ar.addr = slv_qaddr_i;
assign axi_req_o.ar.prot = 3'b0;
assign axi_req_o.ar.region = 4'b0;
assign axi_req_o.ar.size = slv_qsize_i;
assign axi_req_o.ar.len = slv_qrlen_i;
assign axi_req_o.ar.burst = axi_pkg::BURST_INCR;
assign axi_req_o.ar.lock = 1'b0;
assign axi_req_o.ar.cache = axi_pkg::CACHE_MODIFIABLE;
assign axi_req_o.ar.qos = 4'b0;
assign axi_req_o.ar.id = '0;
assign axi_req_o.ar.user = '0;
assign axi_req_o.ar_valid = ~read_full & slv_qvalid_i & ~slv_qwrite_i;
assign slv_perror_o = (axi_resp_i.r.resp inside {axi_pkg::RESP_EXOKAY, axi_pkg::RESP_OKAY}) ? 1'b0 : 1'b1;
assign slv_plast_o = axi_resp_i.r.last;
assign slv_pvalid_o = axi_resp_i.r_valid;
assign axi_req_o.r_ready = slv_pready_i;
assign slv_qready_o = (axi_resp_i.ar_ready & axi_req_o.ar_valid)
| (axi_resp_i.aw_ready & axi_req_o.aw_valid);
`ifndef VERILATOR
// pragma translate_off
hot_one : assert property (
@(posedge clk_i) disable iff (!rst_ni) (slv_qvalid_i & slv_qwrite_i & slv_qready_o) |-> (slv_qrlen_i == 0))
else $warning("Bursts are not supported for write transactions");
// pragma translate_on
`endif
endmodule
// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
/// Arbitrates request/response interface
/// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
/// Demux based on arbitration
module snitch_demux #(
parameter int unsigned NrPorts = 4,
parameter type req_t = snitch_pkg::dreq_t,
parameter type resp_t = snitch_pkg::dresp_t,
parameter int unsigned RespDepth = 8,
parameter bit [NrPorts-1:0] RegisterReq = '0,
parameter Arbiter = "rr" // "rr" or "prio"
) (
input logic clk_i,
input logic rst_ni,
// request port
input req_t [NrPorts-1:0] req_payload_i,
input logic [NrPorts-1:0] req_valid_i,
output logic [NrPorts-1:0] req_ready_o,
output resp_t [NrPorts-1:0] resp_payload_o,
output logic [NrPorts-1:0] resp_last_o,
output logic [NrPorts-1:0] resp_valid_o,
input logic [NrPorts-1:0] resp_ready_i,
// response port
output req_t req_payload_o,
output logic req_valid_o,
input logic req_ready_i,
input resp_t resp_payload_i,
input logic resp_last_i,
input logic resp_valid_i,
output logic resp_ready_o
);
localparam LogNrPorts = (NrPorts > 1) ? $clog2(NrPorts) : 1;
logic [NrPorts-1:0] req_valid_masked;
logic [NrPorts-1:0] req_ready_masked;
logic [LogNrPorts-1:0] idx, idx_rsp;
logic full;
req_t [NrPorts-1:0] req_payload_q;
logic [NrPorts-1:0] req_valid_q;
logic [NrPorts-1:0] req_ready_q;
// Cut the incoming path
for (genvar i = 0; i < NrPorts; i++) begin : gen_spill_regs
spill_register #(
.T ( req_t ),
.Bypass ( !RegisterReq[i] )
) i_spill_register_tcdm_req (
.clk_i,
.rst_ni,
.valid_i ( req_valid_i [i] ),
.ready_o ( req_ready_o [i] ),
.data_i ( req_payload_i [i] ),
.valid_o ( req_valid_q [i] ),
.ready_i ( req_ready_masked [i] ),
.data_o ( req_payload_q [i] )
);
end
for (genvar i = 0; i < NrPorts; i++) begin : gen_req_valid_masked
assign req_valid_masked[i] = req_valid_q[i] & ~full;
assign req_ready_masked[i] = req_ready_q[i] & ~full;
end
/// Arbitrate on instruction request port
stream_arbiter #(
.DATA_T ( req_t ),
.N_INP ( NrPorts ),
.ARBITER ( Arbiter )
) i_stream_arbiter_req (
.clk_i,
.rst_ni,
.inp_data_i ( req_payload_q ),
.inp_valid_i ( req_valid_masked ),
.inp_ready_o ( req_ready_q ),
.oup_data_o ( req_payload_o ),
.oup_valid_o ( req_valid_o ),
.oup_ready_i ( req_ready_i )
);
if (NrPorts == 1) begin
assign idx_rsp = 0;
assign full = 1'b0;
end else begin
onehot_to_bin #(
.ONEHOT_WIDTH ( NrPorts )
) i_onehot_to_bin (
.onehot ( req_valid_q & req_ready_q ),
.bin ( idx )
);
fifo_v3 #(
.DATA_WIDTH ( LogNrPorts ),
.DEPTH ( RespDepth )
) i_resp_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( full ),
.empty_o ( ),
.usage_o ( ),
.data_i ( idx ),
// only reads will generate a response message
.push_i ( req_valid_o & req_ready_i & ~req_payload_o.write ),
.data_o ( idx_rsp ),
.pop_i ( resp_ready_o & resp_valid_i & resp_last_i )
);
end
stream_demux #(
.N_OUP ( NrPorts )
) i_stream_demux_resp (
.inp_valid_i ( resp_valid_i ),
.inp_ready_o ( resp_ready_o ),
.oup_sel_i ( idx_rsp ),
.oup_valid_o ( resp_valid_o ),
.oup_ready_i ( resp_ready_i )
);
for (genvar i = 0; i < NrPorts; i++) begin
assign resp_payload_o[i] = resp_payload_i;
assign resp_last_o[i] = resp_last_i;
end
endmodule
// 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 <fschuiki@iis.ee.ethz.ch>
/// A linear feedback shift register.
///
/// The register provides a maximum length sequence for N <= 32. For larger N,
/// multiple LFSR are instantiated. Note that the generated sequence is forced
/// to include the value 0, making it length 2**N instead of the usual 2**N-1.
module snitch_icache_lfsr #(
parameter int N = -1
)(
input logic clk_i,
input logic rst_ni,
output logic [N-1:0] value_o,
input logic enable_i
);
`ifndef SYNTHESIS
initial assert(N > 0);
`endif
if (N > 32) begin : g_split
localparam int N0 = N/2;
localparam int N1 = N-N0;
snitch_icache_lfsr #(N0) i_lo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.value_o ( value_o[N0-1:0] ),
.enable_i ( enable_i )
);
snitch_icache_lfsr #(N1) i_hi (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.value_o ( value_o[N-1:N0] ),
.enable_i ( enable_i && value_o[N0-1:0] == 0 )
);
end else if (N == 1) begin : g_toggle
logic q;
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni)
q <= 0;
else if (enable_i)
q <= ~q;
end
assign value_o = q;
end else begin : g_impl
logic [N-1:0] q, d, taps;
assign value_o = q;
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni)
q <= 0;
else if (enable_i)
q <= d;
end
always_comb begin
if (q == '0) begin
d = '1;
end else begin
d = {1'b0, q[N-1:1]};
if (q[0]) d ^= taps;
if (d == '1) d = '0;
end
end
// A lookup table for the taps.
always_comb begin
taps = 1 << (N-1);
case (N)
2: taps = $unsigned( 1<< 1 | 1<< 0 );
3: taps = $unsigned( 1<< 2 | 1<< 1 );
4: taps = $unsigned( 1<< 3 | 1<< 2 );
5: taps = $unsigned( 1<< 4 | 1<< 2 );
6: taps = $unsigned( 1<< 5 | 1<< 4 );
7: taps = $unsigned( 1<< 6 | 1<< 5 );
8: taps = $unsigned( 1<< 7 | 1<< 5 | 1<< 4 | 1<< 3 );
9: taps = $unsigned( 1<< 8 | 1<< 4 );
10: taps = $unsigned( 1<< 9 | 1<< 6 );
11: taps = $unsigned( 1<<10 | 1<< 8 );
12: taps = $unsigned( 1<<11 | 1<<10 | 1<< 9 | 1<< 3 );
13: taps = $unsigned( 1<<12 | 1<<11 | 1<<10 | 1<< 7 );
14: taps = $unsigned( 1<<13 | 1<<12 | 1<<11 | 1<< 1 );
15: taps = $unsigned( 1<<14 | 1<<13 );
16: taps = $unsigned( 1<<15 | 1<<14 | 1<<12 | 1<< 3 );
17: taps = $unsigned( 1<<16 | 1<<13 );
18: taps = $unsigned( 1<<17 | 1<<10 );
19: taps = $unsigned( 1<<18 | 1<<17 | 1<<16 | 1<<13 );
20: taps = $unsigned( 1<<19 | 1<<16 );
21: taps = $unsigned( 1<<20 | 1<<18 );
22: taps = $unsigned( 1<<21 | 1<<20 );
23: taps = $unsigned( 1<<22 | 1<<17 );
24: taps = $unsigned( 1<<23 | 1<<22 | 1<<21 | 1<<16 );
25: taps = $unsigned( 1<<24 | 1<<21 );
26: taps = $unsigned( 1<<25 | 1<< 5 | 1<< 1 | 1<< 0 );
27: taps = $unsigned( 1<<26 | 1<< 4 | 1<< 1 | 1<< 0 );
28: taps = $unsigned( 1<<27 | 1<<24 );
29: taps = $unsigned( 1<<28 | 1<<26 );
30: taps = $unsigned( 1<<29 | 1<< 5 | 1<< 3 | 1<< 0 );
31: taps = $unsigned( 1<<30 | 1<<27 );
32: taps = $unsigned( 1<<31 | 1<<21 | 1<< 1 | 1<< 0 );
endcase;
end
end
endmodule
// 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 <fschuiki@iis.ee.ethz.ch>
package snitch_icache_pkg;
typedef struct packed {
logic l0_miss;
logic l0_hit;
logic l0_prefetch;
logic l0_double_hit;
} icache_events_t;
typedef struct packed {
// Parameters passed to the root module.
int NR_FETCH_PORTS;
int LINE_WIDTH;
int LINE_COUNT;
int SET_COUNT;
int PENDING_COUNT;
int L0_LINE_COUNT;
int FETCH_AW;
int FETCH_DW;
int FILL_AW;
int FILL_DW;
bit L1_TAG_SCM;
bit EARLY_LATCH;
// Derived values.
int FETCH_ALIGN;
int FILL_ALIGN;
int LINE_ALIGN;
int COUNT_ALIGN;
int SET_ALIGN;
int TAG_WIDTH;
int L0_TAG_WIDTH;
int L0_EARLY_TAG_WIDTH;
int ID_WIDTH_REQ;
int ID_WIDTH_RESP;
int PENDING_IW; // refill ID width
} config_t;
endpackage
// 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 <fschuiki@iis.ee.ethz.ch>
/// A refiller for cache lines.
module snitch_icache_refill #(
parameter snitch_icache_pkg::config_t CFG = '0
) (
input logic clk_i,
input logic rst_ni,
input logic [CFG.FETCH_AW-1:0] in_req_addr_i,
input logic [CFG.PENDING_IW-1:0] in_req_id_i,
input logic in_req_bypass_i,
input logic in_req_valid_i,
output logic in_req_ready_o,
output logic [CFG.LINE_WIDTH-1:0] in_rsp_data_o,
output logic in_rsp_error_o,
output logic [CFG.PENDING_IW-1:0] in_rsp_id_o,
output logic in_rsp_bypass_o,
output logic in_rsp_valid_o,
input logic in_rsp_ready_i,
output logic [CFG.FILL_AW-1:0] refill_qaddr_o,
output logic [7:0] refill_qlen_o,
output logic refill_qvalid_o,
input logic refill_qready_i,
input logic [CFG.FILL_DW-1:0] refill_pdata_i,
input logic refill_perror_i,
input logic refill_plast_i,
input logic refill_pvalid_i,
output logic refill_pready_o
);
`ifndef SYNTHESIS
initial assert(CFG != '0);
`endif
// How many response beats are necessary to refill one cache line.
localparam BEATS_PER_REFILL = CFG.LINE_WIDTH >= CFG.FILL_DW ? CFG.LINE_WIDTH/CFG.FILL_DW : 1;
// The response queue holds metadata for the issued requests in order.
logic queue_full;
logic queue_push;
logic queue_pop;
fifo_v3 #(
.DEPTH ( 4 ),
.DATA_WIDTH ( CFG.PENDING_IW+1 )
) i_fifo_id_queue (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( queue_full ),
.empty_o ( ),
.usage_o ( ),
.data_i ( {in_req_bypass_i, in_req_id_i} ),
.push_i ( queue_push ),
.data_o ( {in_rsp_bypass_o, in_rsp_id_o} ),
.pop_i ( queue_pop )
);
// Accept incoming requests, push the ID into the queue, and issue the
// corresponding request.
assign refill_qaddr_o = in_req_addr_i;
assign refill_qlen_o = $unsigned(BEATS_PER_REFILL-1);
assign refill_qvalid_o = in_req_valid_i & ~queue_full;
assign in_req_ready_o = refill_qready_i & ~queue_full;
assign queue_push = refill_qvalid_o & refill_qready_i;
// Assemble incoming responses if the cache line is wider than the bus data width.
logic [CFG.LINE_WIDTH-1:0] response_data;
if (CFG.LINE_WIDTH > CFG.FILL_DW) begin : g_data_concat
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni)
response_data[CFG.LINE_WIDTH-CFG.FILL_DW-1:0] <= '0;
else if (refill_pvalid_i && refill_pready_o)
response_data[CFG.LINE_WIDTH-CFG.FILL_DW-1:0] <= response_data[CFG.LINE_WIDTH-1:CFG.FILL_DW];
end
assign response_data[CFG.LINE_WIDTH-1:CFG.LINE_WIDTH-CFG.FILL_DW] = refill_pdata_i;
end else if (CFG.LINE_WIDTH < CFG.FILL_DW) begin : g_data_slice
assign response_data = refill_pdata_i >> (in_req_addr_i[CFG.FILL_ALIGN-1:CFG.LINE_ALIGN] * CFG.LINE_WIDTH);
end else begin : g_data_passthrough
assign response_data = refill_pdata_i;
end
// Accept response beats. Upon the last beat, pop the ID off the queue
// and return the response.
always_comb begin : p_response
in_rsp_data_o = response_data;
in_rsp_error_o = refill_perror_i;
in_rsp_valid_o = 0;
queue_pop = 0;
refill_pready_o = 0;
if (refill_pvalid_i) begin
if (!refill_plast_i) begin
refill_pready_o = 1;
end else begin
in_rsp_valid_o = 1;
if (in_rsp_ready_i) begin
refill_pready_o = 1;
queue_pop = 1;
end
end
end
end
endmodule
// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Description: Load Store Unit (can handle `NumOutstandingLoads` outstanding loads) and
// optionally NaNBox if used in a floating-point setting.
// It supports out-of-order memory responses via metadata linking with IDs.
module snitch_lsu
import cf_math_pkg::idx_width;
#(
parameter type tag_t = logic [4:0],
parameter int unsigned NumOutstandingLoads = 1,
parameter bit NaNBox = 0,
// Dependent parameters. DO NOT CHANGE.
localparam int unsigned IdWidth = idx_width(NumOutstandingLoads)
) (
input logic clk_i,
input logic rst_i,
// request channel
input tag_t lsu_qtag_i,
input logic lsu_qwrite,
input logic lsu_qsigned,
input logic [31:0] lsu_qaddr_i,
input logic [31:0] lsu_qdata_i,
input logic [1:0] lsu_qsize_i,
input logic [3:0] lsu_qamo_i,
input logic lsu_qvalid_i,
output logic lsu_qready_o,
// response channel
output logic [31:0] lsu_pdata_o,
output tag_t lsu_ptag_o,
output logic lsu_perror_o,
output logic lsu_pvalid_o,
input logic lsu_pready_i,
// Memory Interface Channel
output logic [31:0] data_qaddr_o,
output logic data_qwrite_o,
output logic [3:0] data_qamo_o,
output logic [31:0] data_qdata_o,
output logic [3:0] data_qstrb_o,
output logic [IdWidth-1:0] data_qid_o,
output logic data_qvalid_o,
input logic data_qready_i,
input logic [31:0] data_pdata_i,
input logic data_perror_i,
input logic [IdWidth-1:0] data_pid_i,
input logic data_pvalid_i,
output logic data_pready_o
);
// ----------------
// TYPEDEFS
// ----------------
typedef logic [IdWidth-1:0] meta_id_t;
typedef struct packed {
tag_t tag;
logic sign_ext;
logic [1:0] offset;
logic [1:0] size;
} metadata_t;
// ----------------
// SIGNALS
// ----------------
// ID Table
logic [NumOutstandingLoads-1:0] id_available_d, id_available_q;
metadata_t [NumOutstandingLoads-1:0] metadata_d, metadata_q;
metadata_t req_metadata;
metadata_t resp_metadata;
meta_id_t req_id;
meta_id_t resp_id;
logic id_table_push;
logic id_table_pop;
logic id_table_full;
// Response
logic [31:0] ld_result;
// ----------------
// ID TABLE
// ----------------
// Track ID availability and store metadata
always_comb begin
// Default
id_available_d = id_available_q;
metadata_d = metadata_q;
// Take ID and store metadata
if (id_table_push) begin
id_available_d[req_id] = 1'b0;
metadata_d[req_id] = req_metadata;
end
// Free ID
if (id_table_pop) begin
id_available_d[resp_id] = 1'b1;
end
end
assign req_metadata = '{
tag: lsu_qtag_i,
sign_ext: lsu_qsigned,
offset: lsu_qaddr_i[1:0],
size: lsu_qsize_i
};
assign resp_metadata = metadata_q[resp_id];
// Search available ID for request
lzc #(
.WIDTH ( NumOutstandingLoads )
) i_req_id (
.in_i ( id_available_q ),
.cnt_o ( req_id ),
.empty_o( id_table_full )
);
// Pop if response accepted
assign id_table_pop = data_pvalid_i & data_pready_o;
// Push if load request accepted
assign id_table_push = ~lsu_qwrite & data_qready_i & data_qvalid_o;
// ----------------
// REQUEST
// ----------------
// only make a request when we got a valid request and if it is a load
// also check that we can actually store the necessary information to process
// it in the upcoming cycle(s).
assign data_qvalid_o = (lsu_qvalid_i) & (lsu_qwrite | ~id_table_full);
assign data_qwrite_o = lsu_qwrite;
assign data_qaddr_o = {lsu_qaddr_i[31:2], 2'b0};
assign data_qamo_o = lsu_qamo_i;
assign data_qid_o = req_id;
// generate byte enable mask
always_comb begin
unique case (lsu_qsize_i)
2'b00: data_qstrb_o = (4'b1 << lsu_qaddr_i[1:0]);
2'b01: data_qstrb_o = (4'b11 << lsu_qaddr_i[1:0]);
2'b10: data_qstrb_o = '1;
default: data_qstrb_o = '0;
endcase
end
// re-align write data
/* verilator lint_off WIDTH */
always_comb begin
unique case (lsu_qaddr_i[1:0])
2'b00: data_qdata_o = lsu_qdata_i;
2'b01: data_qdata_o = {lsu_qdata_i[23:0], lsu_qdata_i[31:24]};
2'b10: data_qdata_o = {lsu_qdata_i[15:0], lsu_qdata_i[31:16]};
2'b11: data_qdata_o = {lsu_qdata_i[ 7:0], lsu_qdata_i[31: 8]};
default: data_qdata_o = lsu_qdata_i;
endcase
end
/* verilator lint_on WIDTH */
// the interface didn't accept our request yet
assign lsu_qready_o = ~(data_qvalid_o & ~data_qready_i) & ~id_table_full;
// ----------------
// RESPONSE
// ----------------
// Return Path
// shift the load data back by offset bytes
logic [31:0] shifted_data;
assign shifted_data = data_pdata_i >> {resp_metadata.offset, 3'b000};
always_comb begin
unique case (resp_metadata.size)
2'b00: ld_result = {{24{shifted_data[ 7] & resp_metadata.sign_ext}}, shifted_data[7:0]};
2'b01: ld_result = {{16{shifted_data[15] & resp_metadata.sign_ext}}, shifted_data[15:0]};
2'b10: ld_result = shifted_data;
default: ld_result = shifted_data;
endcase
end
assign resp_id = data_pid_i;
assign lsu_perror_o = data_perror_i;
assign lsu_pdata_o = ld_result;
assign lsu_ptag_o = resp_metadata.tag;
assign lsu_pvalid_o = data_pvalid_i;
assign data_pready_o = lsu_pready_i;
// ----------------
// SEQUENTIAL
// ----------------
always_ff @(posedge clk_i or posedge rst_i) begin
if (rst_i) begin
id_available_q <= '1;
metadata_q <= 'b0;
end else begin
id_available_q <= id_available_d;
metadata_q <= metadata_d;
end
end
// ----------------
// ASSERTIONS
// ----------------
// Check for unsupported parameters
if (NumOutstandingLoads == 0)
$error(1, "[snitch_lsu] NumOutstandingLoads cannot be 0.");
// pragma translate_off
`ifndef VERILATOR
invalid_req_id : assert property(
@(posedge clk_i) disable iff (rst_i) (!(id_table_push & ~id_available_q[req_id])))
else $fatal (1, "Request ID is not available.");
`endif
`ifndef VERILATOR
invalid_resp_id : assert property(
@(posedge clk_i) disable iff (rst_i) (!(id_table_pop & id_available_q[resp_id])))
else $fatal (1, "Response ID does not match with valid metadata.");
`endif
// pragma translate_on
endmodule
// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
/// Hardware implementation of SystemVerilog's `$onehot()` function.
/// It uses a tree of half adders and a separate
/// or reduction tree for the carry.
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Author: Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// Author: Stefan Mach <smach@iis.ee.ethz.ch>
module onehot #(
parameter int unsigned Width = 4
) (
input logic [Width-1:0] d_i,
output logic is_onehot_o
);
// trivial base case
if (Width == 1) begin : gen_degenerated_onehot
assign is_onehot_o = d_i;
end else begin : gen_onehot
localparam int LVLS = $clog2(Width) + 1;
logic [LVLS-1:0][2**(LVLS-1)-1:0] sum, carry;
logic [LVLS-2:0] carry_array;
// Extend to a power of two.
assign sum[0] = $unsigned(d_i);
// generate half adders for each lvl
// lvl 0 is the input level
for (genvar i = 1; i < LVLS; i++) begin
localparam LVL_WIDTH = 2**LVLS / 2**i;
for (genvar j = 0; j < LVL_WIDTH; j+=2) begin
assign sum[i][j/2] = sum[i-1][j] ^ sum[i-1][j+1];
assign carry[i][j/2] = sum[i-1][j] & sum[i-1][j+1];
end
// generate carry tree
assign carry_array[i-1] = |carry[i][LVL_WIDTH/2-1:0];
end
assign is_onehot_o = sum[LVLS-1][0] & ~|carry_array;
end
endmodule
// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
/// Snitch Configuration.
package snitch_pkg;
import cf_math_pkg::idx_width;
localparam DataWidth = 32;
localparam StrbWidth = DataWidth/8;
localparam int NumFPOutstandingLoads = 4;
// Use a high number of outstanding loads, if running a latency-throughput analysis
localparam int NumIntOutstandingLoads = `ifdef TRAFFIC_GEN 2048 `else 8 `endif;
localparam MetaIdWidth = idx_width(NumIntOutstandingLoads);
// Xpulpimg extension enabled?
localparam bit XPULPIMG = `ifdef XPULPIMG `XPULPIMG `else 1'bX `endif;
typedef logic [31:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [StrbWidth-1:0] strb_t;
typedef logic [MetaIdWidth-1:0] meta_id_t;
typedef struct packed {
addr_t BootAddress;
int unsigned NrCores;
} SnitchCfg;
typedef struct packed {
addr_t addr;
meta_id_t id;
logic [3:0] amo;
logic write;
data_t data;
strb_t strb;
} dreq_t;
typedef struct packed {
data_t data;
meta_id_t id;
logic error;
} dresp_t;
typedef struct packed {
addr_t addr;
logic [4:0] id;
logic [31:0] data_op;
data_t data_arga;
data_t data_argb;
data_t data_argc;
} acc_req_t;
typedef struct packed {
logic [4:0] id;
logic error;
data_t data;
} acc_resp_t;
// Number of instructions the sequencer can hold
localparam int FPUSequencerInstr = 16;
// SSRs
localparam logic [31:0] SSR_ADDR_BASE = 32'h20_4800;
localparam logic [31:0] SSR_ADDR_MASK = 32'hffff_fe00;
localparam logic [11:0] CSR_SSR = 12'h7C0;
localparam int SSRNrCredits = 4;
// Registers which are used as SSRs
localparam [4:0] FT0 = 5'b0;
localparam [4:0] FT1 = 5'b1;
localparam [1:0][4:0] SSRRegs = {FT1, FT0};
function automatic logic is_ssr(logic [4:0] register);
unique case (register)
FT0, FT1: return 1'b1;
default : return 0;
endcase
endfunction
// Amount of address bit which should be used for accesses from the SoC side.
// This effectively determines the Address Space of a Snitch Cluster.
localparam logic [31:0] SoCRequestAddrBits = 32;
// Address Map
// TCDM, everything below 0x4000_0000
localparam logic [31:0] TCDMStartAddress = 32'h0000_0000;
localparam logic [31:0] TCDMMask = '1 << 28;
// Slaves on Cluster AXI Bus
typedef enum integer {
TCDM = 0,
ClusterPeripherals = 1,
SoC = 2
} cluster_slave_e;
typedef enum integer {
CoreReq = 0,
ICache = 1,
AXISoC = 2
} cluster_master_e;
localparam int unsigned NrSlaves = 3;
localparam int unsigned NrMasters = 3;
localparam int IdWidth = 2;
localparam int IdWidthSlave = $clog2(NrMasters) + IdWidth;
// 3. SoC 2. Cluster Peripherals 3. TCDM
localparam logic [NrSlaves-1:0][31:0] StartAddress = {32'h8000_0000, 32'h4000_0000, TCDMStartAddress};
localparam logic [NrSlaves-1:0][31:0] EndAddress = {32'hFFFF_FFFF, 32'h5000_0000, TCDMStartAddress + 32'h1000_0000};
localparam logic [NrSlaves-1:0] ValidRule = {{NrSlaves}{1'b1}};
// Cluster Peripheral Registers
typedef enum logic [31:0] {
TCDMStartAddressReg = 32'h4000_0000,
TCDMEndAddressReg = 32'h4000_0008,
NrCoresReg = 32'h4000_0010,
FetchEnableReg = 32'h4000_0018,
ScratchReg = 32'h4000_0020,
WakeUpReg = 32'h4000_0028,
CycleCountReg = 32'h4000_0030,
BarrierReg = 32'h4000_0038,
TcdmAccessedReg = 32'h4000_FFF0,
TcdmCongestedReg = 32'h4000_FFF8,
PerfCounterBase = 32'h4001_0000
} cluster_peripheral_addr_e;
// Offload to shared accelerator
function automatic logic shared_offload (logic [31:0] instr);
logic offload;
unique casez (instr)
riscv_instr::MUL,
riscv_instr::MULH,
riscv_instr::MULHSU,
riscv_instr::MULHU,
riscv_instr::DIV,
riscv_instr::DIVU,
riscv_instr::REM,
riscv_instr::REMU,
riscv_instr::MULW,
riscv_instr::DIVW,
riscv_instr::DIVUW,
riscv_instr::REMW,
riscv_instr::REMUW: offload = 1;
default: offload = 0;
endcase
return offload;
endfunction
// Event strobes per core, counted by the performance counters in the cluster
// peripherals.
typedef struct packed {
logic issue_fpu; // core operations performed in the FPU
logic issue_fpu_seq; // includes load/store operations
logic issue_core_to_fpu; // instructions issued from core to FPU
logic retired_insts; // number of instructions retired by the core
} core_events_t;
endpackage
// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Description: Variable Register File
module snitch_regfile #(
parameter DATA_WIDTH = 32,
parameter NR_READ_PORTS = 2,
parameter NR_WRITE_PORTS = 1,
parameter ZERO_REG_ZERO = 0,
parameter ADDR_WIDTH = 4
) (
// clock and reset
input logic clk_i,
// read port
input logic [NR_READ_PORTS-1:0][ADDR_WIDTH-1:0] raddr_i,
output logic [NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
// write port
input logic [NR_WRITE_PORTS-1:0][ADDR_WIDTH-1:0] waddr_i,
input logic [NR_WRITE_PORTS-1:0][DATA_WIDTH-1:0] wdata_i,
input logic [NR_WRITE_PORTS-1:0] we_i
);
localparam NUM_WORDS = 2**ADDR_WIDTH;
logic [NUM_WORDS-1:0][DATA_WIDTH-1:0] mem;
logic [NR_WRITE_PORTS-1:0][NUM_WORDS-1:0] we_dec;
always_comb begin : we_decoder
for (int unsigned j = 0; j < NR_WRITE_PORTS; j++) begin
for (int unsigned i = 0; i < NUM_WORDS; i++) begin
if (waddr_i[j] == i) we_dec[j][i] = we_i[j];
else we_dec[j][i] = 1'b0;
end
end
end
// loop from 1 to NUM_WORDS-1 as R0 is nil
always_ff @(posedge clk_i) begin : register_write_behavioral
for (int unsigned j = 0; j < NR_WRITE_PORTS; j++) begin
for (int unsigned i = 0; i < NUM_WORDS; i++) begin
if (we_dec[j][i]) begin
mem[i] <= wdata_i[j];
end
end
if (ZERO_REG_ZERO) begin
mem[0] <= '0;
end
end
end
for (genvar i = 0; i < NR_READ_PORTS; i++) begin
assign rdata_o[i] = mem[raddr_i[i]];
end
endmodule
// 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.
//
// Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
/// Wrapper around the flushable spill register to maintain back-ward
/// compatibility.
module spill_register #(
parameter type T = logic,
parameter bit Bypass = 1'b0 // make this spill register transparent
) (
input logic clk_i ,
input logic rst_ni ,
input logic valid_i ,
output logic ready_o ,
input T data_i ,
output logic valid_o ,
input logic ready_i ,
output T data_o
);
spill_register_flushable #(
.T(T),
.Bypass(Bypass)
) spill_register_flushable_i (
.clk_i,
.rst_ni,
.valid_i,
.flush_i(1'b0),
.ready_o,
.data_i,
.valid_o,
.ready_i,
.data_o
);
endmodule
// Copyright 2021 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.
//
// Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
/// A register with handshakes that completely cuts any combinational paths
/// between the input and output. This spill register can be flushed.
module spill_register_flushable #(
parameter type T = logic,
parameter bit Bypass = 1'b0 // make this spill register transparent
) (
input logic clk_i ,
input logic rst_ni ,
input logic valid_i ,
input logic flush_i ,
output logic ready_o ,
input T data_i ,
output logic valid_o ,
input logic ready_i ,
output T data_o
);
if (Bypass) begin : gen_bypass
assign valid_o = valid_i;
assign ready_o = ready_i;
assign data_o = data_i;
end else begin : gen_spill_reg
// The A register.
T a_data_q;
logic a_full_q;
logic a_fill, a_drain;
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_a_data
if (!rst_ni)
a_data_q <= '0;
else if (a_fill)
a_data_q <= data_i;
end
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_a_full
if (!rst_ni)
a_full_q <= 0;
else if (a_fill || a_drain)
a_full_q <= a_fill;
end
// The B register.
T b_data_q;
logic b_full_q;
logic b_fill, b_drain;
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_b_data
if (!rst_ni)
b_data_q <= '0;
else if (b_fill)
b_data_q <= a_data_q;
end
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_b_full
if (!rst_ni)
b_full_q <= 0;
else if (b_fill || b_drain)
b_full_q <= b_fill;
end
// Fill the A register when the A or B register is empty. Drain the A register
// whenever it is full and being filled, or if a flush is requested.
assign a_fill = valid_i && ready_o && (!flush_i);
assign a_drain = (a_full_q && !b_full_q) || flush_i;
// Fill the B register whenever the A register is drained, but the downstream
// circuit is not ready. Drain the B register whenever it is full and the
// downstream circuit is ready, or if a flush is requested.
assign b_fill = a_drain && (!ready_i) && (!flush_i);
assign b_drain = (b_full_q && ready_i) || flush_i;
// We can accept input as long as register B is not full.
// Note: flush_i and valid_i must not be high at the same time,
// otherwise an invalid handshake may occur
assign ready_o = !a_full_q || !b_full_q;
// The unit provides output as long as one of the registers is filled.
assign valid_o = a_full_q | b_full_q;
// We empty the spill register before the slice register.
assign data_o = b_full_q ? b_data_q : a_data_q;
// pragma translate_off
`ifndef VERILATOR
flush_valid : assert property (
@(posedge clk_i) disable iff (~rst_ni) (flush_i |-> ~valid_i)) else
$warning("Trying to flush and feed the spill register simultaneously. You will lose data!");
`endif
// pragma translate_on
end
endmodule
// 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.
// Stream arbiter: Arbitrates a parametrizable number of input streams (i.e., valid-ready
// handshaking with dependency rules as in AXI4) to a single output stream. Once `oup_valid_o` is
// asserted, `oup_data_o` remains invariant until the output handshake has occurred. The
// arbitration scheme is round-robin with "look ahead", see the `rrarbiter` for details.
module stream_arbiter #(
parameter type DATA_T = logic, // Vivado requires a default value for type parameters.
parameter integer N_INP = -1, // Synopsys DC requires a default value for parameters.
parameter ARBITER = "rr" // "rr" or "prio"
) (
input logic clk_i,
input logic rst_ni,
input DATA_T [N_INP-1:0] inp_data_i,
input logic [N_INP-1:0] inp_valid_i,
output logic [N_INP-1:0] inp_ready_o,
output DATA_T oup_data_o,
output logic oup_valid_o,
input logic oup_ready_i
);
stream_arbiter_flushable #(
.DATA_T (DATA_T),
.N_INP (N_INP),
.ARBITER (ARBITER)
) i_arb (
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.inp_data_i (inp_data_i),
.inp_valid_i (inp_valid_i),
.inp_ready_o (inp_ready_o),
.oup_data_o (oup_data_o),
.oup_valid_o (oup_valid_o),
.oup_ready_i (oup_ready_i)
);
endmodule
// 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.
// Stream arbiter: Arbitrates a parametrizable number of input streams (i.e., valid-ready
// handshaking with dependency rules as in AXI4) to a single output stream. Once `oup_valid_o` is
// asserted, `oup_data_o` remains invariant until the output handshake has occurred. The
// arbitration scheme is fair round-robin tree, see `rr_arb_tree` for details.
module stream_arbiter_flushable #(
parameter type DATA_T = logic, // Vivado requires a default value for type parameters.
parameter integer N_INP = -1, // Synopsys DC requires a default value for parameters.
parameter ARBITER = "rr" // "rr" or "prio"
) (
input logic clk_i,
input logic rst_ni,
input logic flush_i,
input DATA_T [N_INP-1:0] inp_data_i,
input logic [N_INP-1:0] inp_valid_i,
output logic [N_INP-1:0] inp_ready_o,
output DATA_T oup_data_o,
output logic oup_valid_o,
input logic oup_ready_i
);
if (ARBITER == "rr") begin : gen_rr_arb
rr_arb_tree #(
.NumIn (N_INP),
.DataType (DATA_T),
.ExtPrio (1'b0),
.AxiVldRdy (1'b1),
.LockIn (1'b1)
) i_arbiter (
.clk_i,
.rst_ni,
.flush_i,
.rr_i ('0),
.req_i (inp_valid_i),
.gnt_o (inp_ready_o),
.data_i (inp_data_i),
.gnt_i (oup_ready_i),
.req_o (oup_valid_o),
.data_o (oup_data_o),
.idx_o ()
);
end else if (ARBITER == "prio") begin : gen_prio_arb
rr_arb_tree #(
.NumIn (N_INP),
.DataType (DATA_T),
.ExtPrio (1'b1),
.AxiVldRdy (1'b1),
.LockIn (1'b1)
) i_arbiter (
.clk_i,
.rst_ni,
.flush_i,
.rr_i ('0),
.req_i (inp_valid_i),
.gnt_o (inp_ready_o),
.data_i (inp_data_i),
.gnt_i (oup_ready_i),
.req_o (oup_valid_o),
.data_o (oup_data_o),
.idx_o ()
);
end else begin : gen_arb_error
// pragma translate_off
$fatal(1, "Invalid value for parameter 'ARBITER'!");
// pragma translate_on
end
endmodule
// 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.
/// Connects the input stream (valid-ready) handshake to one of `N_OUP` output stream handshakes.
///
/// This module has no data ports because stream data does not need to be demultiplexed: the data of
/// the input stream can just be applied at all output streams.
module stream_demux #(
/// Number of connected outputs.
parameter int unsigned N_OUP = 32'd1,
/// Dependent parameters, DO NOT OVERRIDE!
parameter int unsigned LOG_N_OUP = (N_OUP > 32'd1) ? unsigned'($clog2(N_OUP)) : 1'b1
) (
input logic inp_valid_i,
output logic inp_ready_o,
input logic [LOG_N_OUP-1:0] oup_sel_i,
output logic [N_OUP-1:0] oup_valid_o,
input logic [N_OUP-1:0] oup_ready_i
);
always_comb begin
oup_valid_o = '0;
oup_valid_o[oup_sel_i] = inp_valid_i;
end
assign inp_ready_o = oup_ready_i[oup_sel_i];
endmodule
// Copyright (c) 2020 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: Wolfgang Roenninger <wroennin@ethz.ch>
/// Fully connected stream crossbar.
///
/// Handshaking rules as defined by the `AMBA AXI` standard on default.
module stream_xbar #(
/// Number of inputs into the crossbar (`> 0`).
parameter int unsigned NumInp = 32'd0,
/// Number of outputs from the crossbar (`> 0`).
parameter int unsigned NumOut = 32'd0,
/// Data width of the stream. Can be overwritten by defining the type parameter `payload_t`.
parameter int unsigned DataWidth = 32'd1,
/// Payload type of the data ports, only usage of parameter `DataWidth`.
parameter type payload_t = logic [DataWidth-1:0],
/// Adds a spill register stage at each output.
parameter bit OutSpillReg = 1'b0,
/// Use external priority for the individual `rr_arb_trees`.
parameter int unsigned ExtPrio = 1'b0,
/// Use strict AXI valid ready handshaking.
/// To be protocol conform also the parameter `LockIn` has to be set.
parameter int unsigned AxiVldRdy = 1'b1,
/// Lock in the arbitration decision of the `rr_arb_tree`.
/// When this is set, valids have to be asserted until the corresponding transaction is indicated
/// by ready.
parameter int unsigned LockIn = 1'b1,
/// Derived parameter, do **not** overwrite!
///
/// Width of the output selection signal.
parameter int unsigned SelWidth = (NumOut > 32'd1) ? unsigned'($clog2(NumOut)) : 32'd1,
/// Derived parameter, do **not** overwrite!
///
/// Signal type definition for selecting the output at the inputs.
parameter type sel_oup_t = logic[SelWidth-1:0],
/// Derived parameter, do **not** overwrite!
///
/// Width of the input index signal.
parameter int unsigned IdxWidth = (NumInp > 32'd1) ? unsigned'($clog2(NumInp)) : 32'd1,
/// Derived parameter, do **not** overwrite!
///
/// Signal type definition indicating from which input the output came.
parameter type idx_inp_t = logic[IdxWidth-1:0]
) (
/// Clock, positive edge triggered.
input logic clk_i,
/// Asynchronous reset, active low.
input logic rst_ni,
/// Flush the state of the internal `rr_arb_tree` modules.
/// If not used set to `0`.
/// Flush should only be used if there are no active `valid_i`, otherwise it will
/// not adhere to the AXI handshaking.
input logic flush_i,
/// Provide an external state for the `rr_arb_tree` models.
/// Will only do something if ExtPrio is `1` otherwise tie to `0`.
input idx_inp_t [NumOut-1:0] rr_i,
/// Input data ports.
/// Has to be stable as long as `valid_i` is asserted when parameter `AxiVldRdy` is set.
input payload_t [NumInp-1:0] data_i,
/// Selection of the output port where the data should be routed.
/// Has to be stable as long as `valid_i` is asserted and parameter `AxiVldRdy` is set.
input sel_oup_t [NumInp-1:0] sel_i,
/// Input is valid.
input logic [NumInp-1:0] valid_i,
/// Input is ready to accept data.
output logic [NumInp-1:0] ready_o,
/// Output data ports. Valid if `valid_o = 1`
output payload_t [NumOut-1:0] data_o,
/// Index of the input port where data came from.
output idx_inp_t [NumOut-1:0] idx_o,
/// Output is valid.
output logic [NumOut-1:0] valid_o,
/// Output can be accepted.
input logic [NumOut-1:0] ready_i
);
typedef struct packed {
payload_t data;
idx_inp_t idx;
} spill_data_t;
logic [NumInp-1:0][NumOut-1:0] inp_valid;
logic [NumInp-1:0][NumOut-1:0] inp_ready;
payload_t [NumOut-1:0][NumInp-1:0] out_data;
logic [NumOut-1:0][NumInp-1:0] out_valid;
logic [NumOut-1:0][NumInp-1:0] out_ready;
// Generate the input selection
for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_inps
stream_demux #(
.N_OUP ( NumOut )
) i_stream_demux (
.inp_valid_i ( valid_i[i] ),
.inp_ready_o ( ready_o[i] ),
.oup_sel_i ( sel_i[i] ),
.oup_valid_o ( inp_valid[i] ),
.oup_ready_i ( inp_ready[i] )
);
// Do the switching cross of the signals.
for (genvar j = 0; unsigned'(j) < NumOut; j++) begin : gen_cross
// Propagate the data from this input to all outputs.
assign out_data[j][i] = data_i[i];
// switch handshaking
assign out_valid[j][i] = inp_valid[i][j];
assign inp_ready[i][j] = out_ready[j][i];
end
end
// Generate the output arbitration.
for (genvar j = 0; unsigned'(j) < NumOut; j++) begin : gen_outs
spill_data_t arb;
logic arb_valid, arb_ready;
rr_arb_tree #(
.NumIn ( NumInp ),
.DataType ( payload_t ),
.ExtPrio ( ExtPrio ),
.AxiVldRdy ( AxiVldRdy ),
.LockIn ( LockIn )
) i_rr_arb_tree (
.clk_i,
.rst_ni,
.flush_i,
.rr_i ( rr_i[j] ),
.req_i ( out_valid[j] ),
.gnt_o ( out_ready[j] ),
.data_i ( out_data[j] ),
.req_o ( arb_valid ),
.gnt_i ( arb_ready ),
.data_o ( arb.data ),
.idx_o ( arb.idx )
);
spill_data_t spill;
spill_register #(
.T ( spill_data_t ),
.Bypass ( !OutSpillReg )
) i_spill_register (
.clk_i,
.rst_ni,
.valid_i ( arb_valid ),
.ready_o ( arb_ready ),
.data_i ( arb ),
.valid_o ( valid_o[j] ),
.ready_i ( ready_i[j] ),
.data_o ( spill )
);
// Assign the outputs (deaggregate the data).
assign data_o[j] = spill.data;
assign idx_o[j] = spill.idx;
end
// Assertions
// Make sure that the handshake and payload is stable
// pragma translate_off
`ifndef VERILATOR
default disable iff rst_ni;
for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_sel_assertions
assert property (@(posedge clk_i) (valid_i[i] |-> sel_i[i] < sel_oup_t'(NumOut))) else
$fatal(1, "Non-existing output is selected!");
end
if (AxiVldRdy) begin : gen_handshake_assertions
for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_inp_assertions
assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> $stable(data_i[i]))) else
$error("data_i is unstable at input: %0d", i);
assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> $stable(sel_i[i]))) else
$error("sel_i is unstable at input: %0d", i);
assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> valid_i[i])) else
$error("valid_i at input %0d has been taken away without a ready.", i);
end
for (genvar i = 0; unsigned'(i) < NumOut; i++) begin : gen_out_assertions
assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> $stable(data_o[i]))) else
$error("data_o is unstable at output: %0d Check that parameter LockIn is set.", i);
assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> $stable(idx_o[i]))) else
$error("idx_o is unstable at output: %0d Check that parameter LockIn is set.", i);
assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> valid_o[i])) else
$error("valid_o at output %0d has been taken away without a ready.", i);
end
end
initial begin : proc_parameter_assertions
assert (NumInp > 32'd0) else $fatal(1, "NumInp has to be > 0!");
assert (NumOut > 32'd0) else $fatal(1, "NumOut has to be > 0!");
end
`endif
// pragma translate_on
endmodule
// Copyright 2019 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.
module tc_clk_and2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
assign clk_o = clk0_i & clk1_i;
endmodule
module tc_clk_buffer (
input logic clk_i,
output logic clk_o
);
assign clk_o = clk_i;
endmodule
// Description: Behavioral model of an integrated clock-gating cell (ICG)
module tc_clk_gating (
input logic clk_i,
input logic en_i,
input logic test_en_i,
output logic clk_o
);
logic clk_en;
always_latch begin
if (clk_i == 1'b0) clk_en <= en_i | test_en_i;
end
assign clk_o = clk_i & clk_en;
endmodule
module tc_clk_inverter (
input logic clk_i,
output logic clk_o
);
assign clk_o = ~clk_i;
endmodule
module tc_clk_mux2 (
input logic clk0_i,
input logic clk1_i,
input logic clk_sel_i,
output logic clk_o
);
assign clk_o = (clk_sel_i) ? clk1_i : clk0_i;
endmodule
module tc_clk_xor2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
assign clk_o = clk0_i ^ clk1_i;
endmodule
`ifndef SYNTHESIS
module tc_clk_delay #(
parameter int unsigned Delay = 300ps
) (
input logic in_i,
output logic out_o
);
// pragma translate_off
`ifndef VERILATOR
assign #(Delay) out_o = in_i;
`endif
// pragma translate_on
endmodule
`endif
// Copyright 2019 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: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
// Date: 28.05.2019
// Description: Package with important constants and lookup tables for TCDM
// interconnect.
package tcdm_interconnect_pkg;
typedef enum logic [1:0] { LIC, BFLY2, BFLY4, CLOS } topo_e;
////////////////////////////////////////////////////////////////////////
// LUT params for Clos net with configs: 1: m=0.50*n, 2: m=1.00*n, 3: m=2.00*n,
// to be indexed with [config_idx][$clog2(BankingFact)][$clog2(NumBanks)]
// generated with MATLAB script gen_clos_params.m
////////////////////////////////////////////////////////////////////////
localparam logic [3:1][4:0][12:2][15:0] ClosNLut = {16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2};
localparam logic [3:1][4:0][12:2][15:0] ClosMLut = {16'd128,16'd128,16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,
16'd128,16'd128,16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,
16'd128,16'd128,16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,
16'd128,16'd128,16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,
16'd128,16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,16'd1,
16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,16'd1,
16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,16'd1,
16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,16'd1,
16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,16'd1,16'd1};
localparam logic [3:1][4:0][12:2][15:0] ClosRLut = {16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2,16'd2,
16'd64,16'd64,16'd32,16'd32,16'd16,16'd16,16'd8,16'd8,16'd4,16'd4,16'd2};
endpackage : tcdm_interconnect_pkg
// Copyright 2021 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// TCDM Shim
// Description: Converts propper handshaking (ready/valid) to TCDM signaling
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
module tcdm_shim
import mempool_pkg::address_map_t;
import cf_math_pkg::idx_width;
#(
parameter int unsigned AddrWidth = 32 ,
parameter int unsigned DataWidth = 32 ,
parameter int unsigned MaxOutStandingReads = 8 ,
parameter int unsigned NrTCDM = 2 ,
parameter int unsigned NrSoC = 1 ,
parameter int unsigned NumRules = 1 , // Routing rules
localparam int unsigned StrbWidth = DataWidth/8 ,
localparam int unsigned NumOutput = NrTCDM + NrSoC,
localparam int unsigned MetaIdWidth = idx_width(MaxOutStandingReads)
) (
input logic clk_i,
input logic rst_ni,
// to TCDM
output logic [NrTCDM-1:0] tcdm_req_valid_o,
output logic [NrTCDM-1:0][AddrWidth-1:0] tcdm_req_tgt_addr_o,
output logic [NrTCDM-1:0] tcdm_req_wen_o,
output logic [NrTCDM-1:0][DataWidth-1:0] tcdm_req_wdata_o,
output logic [NrTCDM-1:0][3:0] tcdm_req_amo_o,
output logic [NrTCDM-1:0][MetaIdWidth-1:0] tcdm_req_id_o,
output logic [NrTCDM-1:0][StrbWidth-1:0] tcdm_req_be_o,
input logic [NrTCDM-1:0] tcdm_req_ready_i,
input logic [NrTCDM-1:0] tcdm_resp_valid_i,
output logic [NrTCDM-1:0] tcdm_resp_ready_o,
input logic [NrTCDM-1:0][DataWidth-1:0] tcdm_resp_rdata_i,
input logic [NrTCDM-1:0][MetaIdWidth-1:0] tcdm_resp_id_i,
// to SoC
output logic [NrSoC-1:0] [AddrWidth-1:0] soc_qaddr_o,
output logic [NrSoC-1:0] soc_qwrite_o,
output logic [NrSoC-1:0] [3:0] soc_qamo_o,
output logic [NrSoC-1:0] [DataWidth-1:0] soc_qdata_o,
output logic [NrSoC-1:0] [StrbWidth-1:0] soc_qstrb_o,
output logic [NrSoC-1:0] soc_qvalid_o,
input logic [NrSoC-1:0] soc_qready_i,
input logic [NrSoC-1:0] [DataWidth-1:0] soc_pdata_i,
input logic [NrSoC-1:0] soc_perror_i,
input logic [NrSoC-1:0] soc_pvalid_i,
output logic [NrSoC-1:0] soc_pready_o,
// from core
input logic [AddrWidth-1:0] data_qaddr_i,
input logic data_qwrite_i,
input logic [3:0] data_qamo_i,
input logic [DataWidth-1:0] data_qdata_i,
input logic [StrbWidth-1:0] data_qstrb_i,
input logic [MetaIdWidth-1:0] data_qid_i,
input logic data_qvalid_i,
output logic data_qready_o,
output logic [DataWidth-1:0] data_pdata_o,
output logic data_perror_o,
output logic [MetaIdWidth-1:0] data_pid_o,
output logic data_pvalid_o,
input logic data_pready_i,
// Address map
input address_map_t [NumRules-1:0] address_map_i
);
// Imports
import snitch_pkg::dreq_t ;
import snitch_pkg::dresp_t;
// Includes
`include "common_cells/registers.svh"
dreq_t data_qpayload ;
dreq_t [NrSoC-1:0] soc_qpayload ;
dreq_t [NrTCDM-1:0] tcdm_qpayload;
dresp_t data_ppayload ;
dresp_t [NrSoC-1:0] soc_ppayload ;
dresp_t [NrTCDM-1:0] tcdm_ppayload;
for (genvar i = 0; i < NrTCDM; i++) begin : gen_tcdm_ppayload
assign tcdm_ppayload[i].id = tcdm_resp_id_i[i] ;
assign tcdm_ppayload[i].data = tcdm_resp_rdata_i[i];
assign tcdm_ppayload[i].error = 1'b0 ;
end
// ROB IDs of the SoC requests (come back in order)
logic [NrSoC-1:0][MetaIdWidth-1:0] soc_meta_id;
for (genvar i = 0; i < NrSoC; i++) begin: gen_soc_meta_id_fifo
fifo_v3 #(
.DEPTH (MaxOutStandingReads),
.DATA_WIDTH(MetaIdWidth )
) i_soc_meta_id_fifo (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i (1'b0 ),
.testmode_i(1'b0 ),
.data_i (data_qid_i ),
.push_i (soc_qvalid_o[i] & soc_qready_i[i] &!soc_qwrite_o[i]),
.full_o (/* Unused */ ),
.data_o (soc_meta_id[i] ),
.pop_i (soc_pvalid_i[i] & soc_pready_o[i] ),
.empty_o (/* Unused */ ),
.usage_o (/* Unused */ )
);
end: gen_soc_meta_id_fifo
// Demux according to address
snitch_addr_demux #(
.NrOutput (NumOutput),
.AddressWidth (AddrWidth),
.NumRules (NumRules ), // TODO
.req_t (dreq_t ),
.resp_t (dresp_t )
) i_snitch_addr_demux (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.req_addr_i (data_qaddr_i ),
.req_payload_i (data_qpayload ),
.req_valid_i (data_qvalid_i ),
.req_ready_o (data_qready_o ),
.resp_payload_o(data_ppayload ),
.resp_valid_o (data_pvalid_o ),
.resp_ready_i (data_pready_i ),
.req_payload_o ({soc_qpayload, tcdm_qpayload} ),
.req_valid_o ({soc_qvalid_o, tcdm_req_valid_o} ),
.req_ready_i ({soc_qready_i, tcdm_req_ready_i} ),
.resp_payload_i({soc_ppayload, tcdm_ppayload} ),
.resp_valid_i ({soc_pvalid_i, tcdm_resp_valid_i}),
.resp_ready_o ({soc_pready_o, tcdm_resp_ready_o}),
.address_map_i (address_map_i )
);
// Connect TCDM output ports
for (genvar i = 0; i < NrTCDM; i++) begin : gen_tcdm_con
assign tcdm_req_tgt_addr_o[i] = tcdm_qpayload[i].addr ;
assign tcdm_req_wdata_o[i] = tcdm_qpayload[i].data ;
assign tcdm_req_amo_o[i] = tcdm_qpayload[i].amo ;
assign tcdm_req_id_o[i] = tcdm_qpayload[i].id ;
assign tcdm_req_wen_o[i] = tcdm_qpayload[i].write;
assign tcdm_req_be_o[i] = tcdm_qpayload[i].strb ;
end
// Connect SOCs
for (genvar i = 0; i < NrSoC; i++) begin : gen_soc_con
assign soc_qaddr_o[i] = soc_qpayload[i].addr ;
assign soc_qwrite_o[i] = soc_qpayload[i].write;
assign soc_qamo_o[i] = soc_qpayload[i].amo ;
assign soc_qdata_o[i] = soc_qpayload[i].data ;
assign soc_qstrb_o[i] = soc_qpayload[i].strb ;
assign soc_ppayload[i].data = soc_pdata_i[i] ;
assign soc_ppayload[i].id = soc_meta_id[i] ;
assign soc_ppayload[i].error = soc_perror_i[i] ;
end
// Request interface
assign data_qpayload.addr = data_qaddr_i ;
assign data_qpayload.write = data_qwrite_i;
assign data_qpayload.amo = data_qamo_i ;
assign data_qpayload.data = data_qdata_i ;
assign data_qpayload.id = data_qid_i ;
assign data_qpayload.strb = data_qstrb_i ;
// Response interface
assign data_pdata_o = data_ppayload.data ;
assign data_perror_o = data_ppayload.error;
assign data_pid_o = data_ppayload.id ;
// Elaboration-time assertions
if (AddrWidth != 32)
$fatal(1, "[tcdm_shim] Only support 32-bit wide addresses.");
if (DataWidth != 32)
$fatal(1, "[tcdm_shim] Only support a data width of 32 bits.");
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment