Commit 7da26346 by sakundu

Added mempool group latest rtl

Signed-off-by: sakundu <sakundu@ucsd.edu>
parent 9984cf64
# Copyright 2021 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
*.log
*.dasm
*.trace
*.project
bender
.bender/*
.dvt/*
*build*
*results*
results*
log/
plots*
obj_dir
# Hardware Dependencies
All hardware dependencies in this folder are git repositories themselves that are flattened into this repository. They are managed by [Bender](https://github.com/fabianschuiki/bender) instead of git submodules or subtrees. This allows us to have a flat repository where all changes are visible in a single repository. The downside is that changes to the dependencies need to be pulled/pushed to the upstream repository manually. The flow to do so is described here.
## Add new dependency
1. To add a new dependency, simply add it to the `Bender.yml` file and execute `bender update`. This will check the new dependencies and create a new `Bender.lock` entry. You can double check the lock file to make sure the correct commit will be checked out.
2. Execute a bender command, like `bender script vsim`, which will trigger Bender to actually check out the repository.
3. Remove the `.git` folder in the new dependency, which will flatten it into the main repository.
4. Add all the files from the submodule to git and create a commit. Make sure the main repository's `.gitignore` file is not excluding important files from the new dependency.
## Push changes to upstream
If you modify a dependency to implement some fixes or upgrades you want to contribute back to the upstream repository use the following flow. We use the `axi` dependency as an example.
1. Make sure the main repository is in a clean state and everything is checked in.
2. Delete the folder containing your dependency: `rm -rf deps/axi`
3. Check out the folder with Bender. This will restore the folder from the last point you synchronized it with the upstream repository. `make build`
4. Reset the changes in the dependency folder that you just introduced by checking out the last synchronization point. `git checkout deps/axi`
5. Now you restored the `.git` folder linked with the correct commit. However, the remote will be in the `.bender` folder. Add the upstream remote you want to push to. `cd deps/axi; git remote add upstream https://github.com/pulp-platform/axi`
6. Create/Checkout a branch and add the changes, push them to the upstream repository.
7. Remote the `.git` folder in the repo to reflatten the repository and add a commit updating the `Bender.lock` file to indicate the synchronization point.
## Pull changes from upstream
1. Make sure the main repository is in a clean state and everything is checked in.
2. Make sure your dependency has no changes compared to the last synchronization point that are not yet merged with the upstream repository.
3. Delete the folder containing your dependency: `rm -rf deps/axi`
4. Change the `Bender.yml`/`Bender.lock` to the version you want to upgrade to.
5. Pull the dependency through Bender. `make build`
6. Remove the dependency's `.git` folder.
7. Commit the changes to the dependency and the changes to the `Bender.*` files to the main repository.
.bender
work
scripts/compile.tcl
scripts/__pycache__
*.wlf
dma_trace*.log
dma_trace*.txt
dma_transfers*
modelsim.ini
transcript
doc/build
---
packages:
axi:
revision: 442ff3375710513623f95944d66cc2bd09b2f155
version: 0.29.1
source:
Git: "https://github.com/pulp-platform/axi.git"
dependencies:
- common_cells
- common_verification
common_cells:
revision: 015917ff33e5f944e866814f72f2074fb0f4220f
version: 1.22.1
source:
Git: "https://github.com/pulp-platform/common_cells.git"
dependencies:
- common_verification
- tech_cells_generic
common_verification:
revision: 6fc76fb013315af9fabbb90b431863d498df2d6d
version: 0.2.0
source:
Git: "https://github.com/pulp-platform/common_verification.git"
dependencies: []
tech_cells_generic:
revision: 203038f857158ae4634c47ce0281f402cc2a1344
version: 0.2.4
source:
Git: "https://github.com/pulp-platform/tech_cells_generic.git"
dependencies:
- common_verification
package:
name: idma
authors:
- "Thomas Benz <tbenz@iis.ee.ethz.ch>" # current maintainer
dependencies:
common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 }
common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 }
axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.29.1 }
sources:
# Source files grouped in levels. Files in level 0 have no dependencies on files in this
# package. Files in level 1 only depend on files in level 0, files in level 2 on files in
# levels 1 and 0, etc. Files within a level are ordered alphabetically.
# Level 0
- src/axi_dma_data_path.sv
# Level 1
- src/axi_dma_data_mover.sv
- src/axi_dma_burst_reshaper.sv
# Level 2
- src/axi_dma_backend.sv
# Level 3: MemPool
- src/midends/idma_split_midend.sv
- src/midends/idma_distributed_midend.sv
- src/frontends/mempool/mempool_dma_frontend_reg_pkg.sv
- src/frontends/mempool/mempool_dma_frontend_reg_top.sv
- src/frontends/mempool/mempool_dma.sv
- target: test
files:
# Level 0:
- test/fixture_axi_dma_backend.sv
# Level 1:
- test/tb_axi_dma_backend.sv
GIT ?= git
BENDER ?= bender
VSIM ?= vsim
PYTHON ?= python3
all: sim_all
clean: sim_clean
# Ensure half-built targets are purged
.DELETE_ON_ERROR:
# --------------
# RTL SIMULATION
# --------------
VLOG_ARGS += -suppress vlog-2583 -suppress vlog-13314 -suppress vlog-13233 -timescale \"1 ns / 1 ps\"
XVLOG_ARGS += -64bit -compile -vtimescale 1ns/1ns -quiet
define generate_vsim
echo 'set ROOT [file normalize [file dirname [info script]]/$3]' > $1
bender script $(VSIM) --vlog-arg="$(VLOG_ARGS)" $2 | grep -v "set ROOT" >> $1
echo >> $1
endef
sim_all: scripts/compile.tcl
sim_clean:
rm -rf scripts/compile.tcl
rm -rf work
scripts/compile.tcl: Bender.yml
$(call generate_vsim, $@, -t rtl -t test,..)
# --------------
# TRACER
# --------------
trace:
dma_trace_00000.txt
dma_trace_%.txt: scripts/dma_trace.py scripts/dma_backend.py
$(PYTHON) $< dma_trace_$*.log > $@
# iDMA
Home of the DMA. Replaces the version on `github.com/axi`!!!!
## Build for Simulation
`sh> make sim_all`
## Simulate
`sh> vsim -64 &` \
`vsim> source scripts/compile.tcl` \
`vsim> source scripts/start.tcl` \
`vsim> run -all`
### Trace
`sh> make trace`
## Docs
Requires a morty installation for full functionality (See https://github.com/zarubaf/morty)
`make -C doc clean morty-docs html`
# Copyright 2022 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
# Samuel Riedel <sriedel@iis.ee.ethz.ch>
SHELL = /usr/bin/env bash
ROOT_DIR := $(patsubst %/,%, $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
regtool ?= $(ROOT_DIR)/../../../../register_interface/vendor/lowrisc_opentitan/util/regtool.py
RTL := $(patsubst $(ROOT_DIR)/%.hjson,%,$(shell find $(ROOT_DIR) -name "*.hjson"))
all: $(RTL)_reg_top.sv $(RTL).h $(RTL).html
$(RTL)_reg_top.sv: %_reg_top.sv: %.hjson
$(regtool) $^ -r -t $(ROOT_DIR)
$(RTL).h: %.h: %.hjson
$(regtool) $^ -D -o $@
$(RTL).html: %.html: %.hjson
$(regtool) $^ -d -o $@
clean:
@rm -fv $(RTL)_reg_pkg.sv
@rm -fv $(RTL)_reg_top.sv
@rm -fv $(RTL).html
@rm -fv $(RTL).h
# MemPool Front-end
This front-end implements a 'distributed' DMA with a single front-end but multiple back-ends. Each back-end is responsible for one area of the memory, which is interleaved. This means that a mid-end will split up single one-dimensional DMA requests into multiple DMA requests going to the corresponding back-ends.
// Copyright 2022 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Samuel Riedel <sriedel@iis.ee.ethz.ch>
`include "register_interface/typedef.svh"
`include "common_cells/registers.svh"
module mempool_dma #(
parameter int unsigned AddrWidth = 32,
parameter int unsigned DataWidth = 32,
parameter int unsigned NumBackends = 1,
/// AXI4+ATOP request struct definition.
parameter type axi_lite_req_t = logic,
/// AXI4+ATOP response struct definition.
parameter type axi_lite_rsp_t = logic,
/// Arbitrary 1D burst request definition:
/// - `id`: the AXI id used - this id should be constant, as the DMA does not support reordering
/// - `src`, `dst`: source and destination address, same width as the AXI 4 channels
/// - `num_bytes`: the length of the contiguous 1D transfer requested, can be up to 32/64 bit long
/// num_bytes will be interpreted as an unsigned number
/// A value of 0 will cause the backend to discard the transfer prematurely
/// - `src_cache`, `dst_cache`: the configuration of the cache fields in the AX beats
/// - `src_burst`, `dst_burst`: currently only incremental bursts are supported (`2'b01`)
/// - `decouple_rw`: if set to true, there is no longer exactly one AXI write_request issued for
/// every read request. This mode can improve performance of unaligned transfers when crossing
/// the AXI page boundaries.
/// - `deburst`: if set, the DMA will split all bursts in single transfers
/// - `serialize`: if set, the DMA will only send AX belonging to a given Arbitrary 1D burst request
/// at a time. This is default behavior to prevent deadlocks. Setting `serialize` to
/// zero violates the AXI4+ATOP specification.
parameter type burst_req_t = logic,
/// Give each DMA backend a unique id
parameter int unsigned DmaIdWidth = -1
) (
input logic clk_i,
input logic rst_ni,
input axi_lite_req_t config_req_i,
output axi_lite_rsp_t config_res_o,
output burst_req_t burst_req_o,
output logic valid_o,
input logic ready_i,
input logic backend_idle_i,
input logic trans_complete_i,
output logic [DmaIdWidth-1:0] dma_id_o
);
// import mempool_dma_frontend_reg_pkg::BlockAw;
// import mempool_dma_frontend_reg_pkg::mempool_dma_frontend_reg2hw_t;
// import mempool_dma_frontend_reg_pkg::mempool_dma_frontend_hw2reg_t;
import mempool_dma_frontend_reg_pkg::*;
`REG_BUS_TYPEDEF_ALL(ctrl_reg, logic[BlockAw-1:0], logic[DataWidth-1:0], logic[DataWidth/8-1:0]);
ctrl_reg_req_t ctrl_reg_req;
ctrl_reg_rsp_t ctrl_reg_rsp;
mempool_dma_frontend_reg2hw_t ctrl_reg2hw;
mempool_dma_frontend_hw2reg_t ctrl_hw2reg;
logic trans_complete_d, trans_complete_q;
`FF(trans_complete_q, trans_complete_d, '0, clk_i, rst_ni)
always_comb begin
trans_complete_d = trans_complete_q;
if (trans_complete_i) begin
trans_complete_d = 1'b1;
end
if (valid_o) begin
trans_complete_d = 1'b0;
end
end
assign ctrl_hw2reg = '{
status : backend_idle_i,
next_id : 1'b0,
done : trans_complete_q
};
axi_lite_to_reg #(
.ADDR_WIDTH (AddrWidth ),
.DATA_WIDTH (DataWidth ),
.BUFFER_DEPTH (1 ),
.DECOUPLE_W (0 ),
.axi_lite_req_t(axi_lite_req_t),
.axi_lite_rsp_t(axi_lite_rsp_t),
.reg_req_t (ctrl_reg_req_t),
.reg_rsp_t (ctrl_reg_rsp_t)
) i_axi_lite_to_reg (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.axi_lite_req_i(config_req_i),
.axi_lite_rsp_o(config_res_o),
.reg_req_o (ctrl_reg_req),
.reg_rsp_i (ctrl_reg_rsp)
);
mempool_dma_frontend_reg_top #(
.reg_req_t(ctrl_reg_req_t),
.reg_rsp_t(ctrl_reg_rsp_t)
) i_mempool_dma_frontend_reg_top (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.reg_req_i(ctrl_reg_req),
.reg_rsp_o(ctrl_reg_rsp),
.reg2hw (ctrl_reg2hw ),
.hw2reg (ctrl_hw2reg ),
.devmode_i(1'b0 )
);
assign burst_req_o = '{
id : 0,
src : ctrl_reg2hw.src_addr,
dst : ctrl_reg2hw.dst_addr,
num_bytes : ctrl_reg2hw.num_bytes,
cache_src : axi_pkg::CACHE_MODIFIABLE,
cache_dst : axi_pkg::CACHE_MODIFIABLE,
burst_src : axi_pkg::BURST_INCR,
burst_dst : axi_pkg::BURST_INCR,
decouple_rw : 1'b1,
deburst : 1'b0,
serialize : 1'b0
};
always_comb begin
valid_o = '0;
if (ctrl_reg2hw.next_id.re) begin
valid_o = 1'b1;
if (!ready_i) begin
// Store the valid request and stall upstream
// TODO config_res_o.raedy = 0;
end
// TODO stall ctrl_reg_req, ctrl_reg_rsp interface with ready_i
end
end
assign dma_id_o = '0;
// pragma translate_off
integer cycle;
integer transfer;
integer size;
integer f;
string str;
/* verilator lint_off BLKSEQ */
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cycle = 0;
size = 0;
transfer = 0;
end else begin
if (valid_o && ready_i) begin
cycle = 0;
transfer = 1;
size = burst_req_o.num_bytes;
str = $sformatf("[mempool_dma] Launch request of %08d bytes\n", size);
f = $fopen("dma.log", "a");
$fwrite(f, str);
$fclose(f);
end else begin
cycle = cycle + 1;
end
if (transfer == 1 && trans_complete_i == 1) begin
transfer = 0;
str = "[mempool_dma] Finished request\n";
str = $sformatf("%s[mempool_dma] Duration: %08d cycles, %08.8f bytes/cycle\n", str, cycle, size/cycle);
f = $fopen("dma.log", "a");
$fwrite(f, str);
$fclose(f);
end
end
end
// pragma translate_on
endmodule : mempool_dma
// Generated register defines for mempool_dma_frontend
// Copyright information found in source file:
// Copyright 2022 ETH Zurich and University of Bologna.
// Licensing information found in source file:
// Licensed under Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
#ifndef _MEMPOOL_DMA_FRONTEND_REG_DEFS_
#define _MEMPOOL_DMA_FRONTEND_REG_DEFS_
#ifdef __cplusplus
extern "C" {
#endif
// Register width
#define MEMPOOL_DMA_FRONTEND_PARAM_REG_WIDTH 32
// Source Address
#define MEMPOOL_DMA_FRONTEND_SRC_ADDR_REG_OFFSET 0x0
// Destination Address
#define MEMPOOL_DMA_FRONTEND_DST_ADDR_REG_OFFSET 0x4
// Transfer size in bytes
#define MEMPOOL_DMA_FRONTEND_NUM_BYTES_REG_OFFSET 0x8
// Configuration Register for DMA settings
#define MEMPOOL_DMA_FRONTEND_CONF_REG_OFFSET 0xc
#define MEMPOOL_DMA_FRONTEND_CONF_DECOUPLE_BIT 0
#define MEMPOOL_DMA_FRONTEND_CONF_DEBURST_BIT 1
#define MEMPOOL_DMA_FRONTEND_CONF_SERIALIZE_BIT 2
// DMA Status
#define MEMPOOL_DMA_FRONTEND_STATUS_REG_OFFSET 0x10
#define MEMPOOL_DMA_FRONTEND_STATUS_BUSY_BIT 0
// Next ID launches transfer returns 0 if transfer not set up properly.
#define MEMPOOL_DMA_FRONTEND_NEXT_ID_REG_OFFSET 0x14
// Get ID of finished transactions.
#define MEMPOOL_DMA_FRONTEND_DONE_REG_OFFSET 0x18
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _MEMPOOL_DMA_FRONTEND_REG_DEFS_
// End generated register defines for mempool_dma_frontend
\ No newline at end of file
// Copyright 2022 ETH Zurich and University of Bologna.
// Solderpad Hardware License Version 0.51 see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Licensed under Solderpad Hardware License, Version 0.51, see LICENSE for details.
{
name: "mempool_dma_frontend"
clock_primary: "clk_i"
reset_primary: "rst_ni"
bus_interfaces: [
{ protocol: "reg_iface"
direction: "device"
}
]
regwidth: 32
registers: [
{ name: "src_addr"
desc: "Source Address"
swaccess: "rw"
hwaccess: "hro"
fields: [
{ bits: "31:0"
name: "src_addr"
desc: "Source Address"
}
]
}
{ name: "dst_addr"
desc: "Destination Address"
swaccess: "rw"
hwaccess: "hro"
fields: [
{ bits: "31:0"
name: "dst_addr"
desc: "Destination Address"
}
]
}
{ name: "num_bytes"
desc: "Transfer size in bytes"
swaccess: "rw"
hwaccess: "hro"
fields: [
{ bits: "31:0"
name: "num_bytes"
desc: "Transfer size in bytes"
}
]
}
{ name: "conf"
desc: "Configuration Register for DMA settings"
swaccess: "rw"
hwaccess: "hro"
fields: [
{ bits: "0"
name: "decouple"
desc: "Decouple enable"
}
{ bits: "1"
name: "deburst"
desc: "Deburst enable"
}
{ bits: "2"
name: "serialize"
desc: "Serialize enable"
}
]
}
{ name: "status"
desc: "DMA Status"
swaccess: "ro"
hwaccess: "hwo"
hwext: "true"
fields: [
{ bits: "0"
name: "busy"
desc: "DMA busy"
}
]
}
{ name: "next_id"
desc: "Next ID launches transfer returns 0 if transfer not set up properly."
swaccess: "ro"
hwaccess: "hrw"
hwext: "true"
hwre: "true"
fields: [
{ bits: "31:0"
name: "next_id"
desc: "Next ID launches transfer returns 0 if transfer not set up properly."
}
]
}
{ name: "done"
desc: "Get ID of finished transactions."
swaccess: "ro"
hwaccess: "hrw"
hwext: "true"
hwre: "true"
fields: [
{ bits: "31:0"
name: "done"
desc: "Get ID of finished transactions."
}
]
}
]
}
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Register Package auto-generated by `reggen` containing data structure
package mempool_dma_frontend_reg_pkg;
// Address widths within the block
parameter int BlockAw = 5;
////////////////////////////
// Typedefs for registers //
////////////////////////////
typedef struct packed {
logic [31:0] q;
} mempool_dma_frontend_reg2hw_src_addr_reg_t;
typedef struct packed {
logic [31:0] q;
} mempool_dma_frontend_reg2hw_dst_addr_reg_t;
typedef struct packed {
logic [31:0] q;
} mempool_dma_frontend_reg2hw_num_bytes_reg_t;
typedef struct packed {
struct packed {
logic q;
} decouple;
struct packed {
logic q;
} deburst;
struct packed {
logic q;
} serialize;
} mempool_dma_frontend_reg2hw_conf_reg_t;
typedef struct packed {
logic [31:0] q;
logic re;
} mempool_dma_frontend_reg2hw_next_id_reg_t;
typedef struct packed {
logic [31:0] q;
logic re;
} mempool_dma_frontend_reg2hw_done_reg_t;
typedef struct packed {
logic d;
} mempool_dma_frontend_hw2reg_status_reg_t;
typedef struct packed {
logic [31:0] d;
} mempool_dma_frontend_hw2reg_next_id_reg_t;
typedef struct packed {
logic [31:0] d;
} mempool_dma_frontend_hw2reg_done_reg_t;
// Register -> HW type
typedef struct packed {
mempool_dma_frontend_reg2hw_src_addr_reg_t src_addr; // [164:133]
mempool_dma_frontend_reg2hw_dst_addr_reg_t dst_addr; // [132:101]
mempool_dma_frontend_reg2hw_num_bytes_reg_t num_bytes; // [100:69]
mempool_dma_frontend_reg2hw_conf_reg_t conf; // [68:66]
mempool_dma_frontend_reg2hw_next_id_reg_t next_id; // [65:33]
mempool_dma_frontend_reg2hw_done_reg_t done; // [32:0]
} mempool_dma_frontend_reg2hw_t;
// HW -> register type
typedef struct packed {
mempool_dma_frontend_hw2reg_status_reg_t status; // [64:64]
mempool_dma_frontend_hw2reg_next_id_reg_t next_id; // [63:32]
mempool_dma_frontend_hw2reg_done_reg_t done; // [31:0]
} mempool_dma_frontend_hw2reg_t;
// Register offsets
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_SRC_ADDR_OFFSET = 5'h 0;
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_DST_ADDR_OFFSET = 5'h 4;
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_NUM_BYTES_OFFSET = 5'h 8;
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_CONF_OFFSET = 5'h c;
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_STATUS_OFFSET = 5'h 10;
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_NEXT_ID_OFFSET = 5'h 14;
parameter logic [BlockAw-1:0] MEMPOOL_DMA_FRONTEND_DONE_OFFSET = 5'h 18;
// Reset values for hwext registers and their fields
parameter logic [0:0] MEMPOOL_DMA_FRONTEND_STATUS_RESVAL = 1'h 0;
parameter logic [31:0] MEMPOOL_DMA_FRONTEND_NEXT_ID_RESVAL = 32'h 0;
parameter logic [31:0] MEMPOOL_DMA_FRONTEND_DONE_RESVAL = 32'h 0;
// Register index
typedef enum int {
MEMPOOL_DMA_FRONTEND_SRC_ADDR,
MEMPOOL_DMA_FRONTEND_DST_ADDR,
MEMPOOL_DMA_FRONTEND_NUM_BYTES,
MEMPOOL_DMA_FRONTEND_CONF,
MEMPOOL_DMA_FRONTEND_STATUS,
MEMPOOL_DMA_FRONTEND_NEXT_ID,
MEMPOOL_DMA_FRONTEND_DONE
} mempool_dma_frontend_id_e;
// Register width information to check illegal writes
parameter logic [3:0] MEMPOOL_DMA_FRONTEND_PERMIT [7] = '{
4'b 1111, // index[0] MEMPOOL_DMA_FRONTEND_SRC_ADDR
4'b 1111, // index[1] MEMPOOL_DMA_FRONTEND_DST_ADDR
4'b 1111, // index[2] MEMPOOL_DMA_FRONTEND_NUM_BYTES
4'b 0001, // index[3] MEMPOOL_DMA_FRONTEND_CONF
4'b 0001, // index[4] MEMPOOL_DMA_FRONTEND_STATUS
4'b 1111, // index[5] MEMPOOL_DMA_FRONTEND_NEXT_ID
4'b 1111 // index[6] MEMPOOL_DMA_FRONTEND_DONE
};
endpackage
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Register Top module auto-generated by `reggen`
`include "common_cells/assertions.svh"
module mempool_dma_frontend_reg_top #(
parameter type reg_req_t = logic,
parameter type reg_rsp_t = logic,
parameter int AW = 5
) (
input clk_i,
input rst_ni,
input reg_req_t reg_req_i,
output reg_rsp_t reg_rsp_o,
// To HW
output mempool_dma_frontend_reg_pkg::mempool_dma_frontend_reg2hw_t reg2hw, // Write
input mempool_dma_frontend_reg_pkg::mempool_dma_frontend_hw2reg_t hw2reg, // Read
// Config
input devmode_i // If 1, explicit error return for unmapped register access
);
import mempool_dma_frontend_reg_pkg::* ;
localparam int DW = 32;
localparam int DBW = DW/8; // Byte Width
// register signals
logic reg_we;
logic reg_re;
logic [AW-1:0] reg_addr;
logic [DW-1:0] reg_wdata;
logic [DBW-1:0] reg_be;
logic [DW-1:0] reg_rdata;
logic reg_error;
logic addrmiss, wr_err;
logic [DW-1:0] reg_rdata_next;
// Below register interface can be changed
reg_req_t reg_intf_req;
reg_rsp_t reg_intf_rsp;
assign reg_intf_req = reg_req_i;
assign reg_rsp_o = reg_intf_rsp;
assign reg_we = reg_intf_req.valid & reg_intf_req.write;
assign reg_re = reg_intf_req.valid & ~reg_intf_req.write;
assign reg_addr = reg_intf_req.addr;
assign reg_wdata = reg_intf_req.wdata;
assign reg_be = reg_intf_req.wstrb;
assign reg_intf_rsp.rdata = reg_rdata;
assign reg_intf_rsp.error = reg_error;
assign reg_intf_rsp.ready = 1'b1;
assign reg_rdata = reg_rdata_next ;
assign reg_error = (devmode_i & addrmiss) | wr_err;
// Define SW related signals
// Format: <reg>_<field>_{wd|we|qs}
// or <reg>_{wd|we|qs} if field == 1 or 0
logic [31:0] src_addr_qs;
logic [31:0] src_addr_wd;
logic src_addr_we;
logic [31:0] dst_addr_qs;
logic [31:0] dst_addr_wd;
logic dst_addr_we;
logic [31:0] num_bytes_qs;
logic [31:0] num_bytes_wd;
logic num_bytes_we;
logic conf_decouple_qs;
logic conf_decouple_wd;
logic conf_decouple_we;
logic conf_deburst_qs;
logic conf_deburst_wd;
logic conf_deburst_we;
logic conf_serialize_qs;
logic conf_serialize_wd;
logic conf_serialize_we;
logic status_qs;
logic status_re;
logic [31:0] next_id_qs;
logic next_id_re;
logic [31:0] done_qs;
logic done_re;
// Register instances
// R[src_addr]: V(False)
prim_subreg #(
.DW (32),
.SWACCESS("RW"),
.RESVAL (32'h0)
) u_src_addr (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// from register interface
.we (src_addr_we),
.wd (src_addr_wd),
// from internal hardware
.de (1'b0),
.d ('0 ),
// to internal hardware
.qe (),
.q (reg2hw.src_addr.q ),
// to register interface (read)
.qs (src_addr_qs)
);
// R[dst_addr]: V(False)
prim_subreg #(
.DW (32),
.SWACCESS("RW"),
.RESVAL (32'h0)
) u_dst_addr (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// from register interface
.we (dst_addr_we),
.wd (dst_addr_wd),
// from internal hardware
.de (1'b0),
.d ('0 ),
// to internal hardware
.qe (),
.q (reg2hw.dst_addr.q ),
// to register interface (read)
.qs (dst_addr_qs)
);
// R[num_bytes]: V(False)
prim_subreg #(
.DW (32),
.SWACCESS("RW"),
.RESVAL (32'h0)
) u_num_bytes (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// from register interface
.we (num_bytes_we),
.wd (num_bytes_wd),
// from internal hardware
.de (1'b0),
.d ('0 ),
// to internal hardware
.qe (),
.q (reg2hw.num_bytes.q ),
// to register interface (read)
.qs (num_bytes_qs)
);
// R[conf]: V(False)
// F[decouple]: 0:0
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_conf_decouple (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// from register interface
.we (conf_decouple_we),
.wd (conf_decouple_wd),
// from internal hardware
.de (1'b0),
.d ('0 ),
// to internal hardware
.qe (),
.q (reg2hw.conf.decouple.q ),
// to register interface (read)
.qs (conf_decouple_qs)
);
// F[deburst]: 1:1
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_conf_deburst (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// from register interface
.we (conf_deburst_we),
.wd (conf_deburst_wd),
// from internal hardware
.de (1'b0),
.d ('0 ),
// to internal hardware
.qe (),
.q (reg2hw.conf.deburst.q ),
// to register interface (read)
.qs (conf_deburst_qs)
);
// F[serialize]: 2:2
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_conf_serialize (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// from register interface
.we (conf_serialize_we),
.wd (conf_serialize_wd),
// from internal hardware
.de (1'b0),
.d ('0 ),
// to internal hardware
.qe (),
.q (reg2hw.conf.serialize.q ),
// to register interface (read)
.qs (conf_serialize_qs)
);
// R[status]: V(True)
prim_subreg_ext #(
.DW (1)
) u_status (
.re (status_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.status.d),
.qre (),
.qe (),
.q (),
.qs (status_qs)
);
// R[next_id]: V(True)
prim_subreg_ext #(
.DW (32)
) u_next_id (
.re (next_id_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.next_id.d),
.qre (reg2hw.next_id.re),
.qe (),
.q (reg2hw.next_id.q ),
.qs (next_id_qs)
);
// R[done]: V(True)
prim_subreg_ext #(
.DW (32)
) u_done (
.re (done_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.done.d),
.qre (reg2hw.done.re),
.qe (),
.q (reg2hw.done.q ),
.qs (done_qs)
);
logic [6:0] addr_hit;
always_comb begin
addr_hit = '0;
addr_hit[0] = (reg_addr == MEMPOOL_DMA_FRONTEND_SRC_ADDR_OFFSET);
addr_hit[1] = (reg_addr == MEMPOOL_DMA_FRONTEND_DST_ADDR_OFFSET);
addr_hit[2] = (reg_addr == MEMPOOL_DMA_FRONTEND_NUM_BYTES_OFFSET);
addr_hit[3] = (reg_addr == MEMPOOL_DMA_FRONTEND_CONF_OFFSET);
addr_hit[4] = (reg_addr == MEMPOOL_DMA_FRONTEND_STATUS_OFFSET);
addr_hit[5] = (reg_addr == MEMPOOL_DMA_FRONTEND_NEXT_ID_OFFSET);
addr_hit[6] = (reg_addr == MEMPOOL_DMA_FRONTEND_DONE_OFFSET);
end
assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
// Check sub-word write is permitted
always_comb begin
wr_err = (reg_we &
((addr_hit[0] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[0] & ~reg_be))) |
(addr_hit[1] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[1] & ~reg_be))) |
(addr_hit[2] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[2] & ~reg_be))) |
(addr_hit[3] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[3] & ~reg_be))) |
(addr_hit[4] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[4] & ~reg_be))) |
(addr_hit[5] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[5] & ~reg_be))) |
(addr_hit[6] & (|(MEMPOOL_DMA_FRONTEND_PERMIT[6] & ~reg_be)))));
end
assign src_addr_we = addr_hit[0] & reg_we & !reg_error;
assign src_addr_wd = reg_wdata[31:0];
assign dst_addr_we = addr_hit[1] & reg_we & !reg_error;
assign dst_addr_wd = reg_wdata[31:0];
assign num_bytes_we = addr_hit[2] & reg_we & !reg_error;
assign num_bytes_wd = reg_wdata[31:0];
assign conf_decouple_we = addr_hit[3] & reg_we & !reg_error;
assign conf_decouple_wd = reg_wdata[0];
assign conf_deburst_we = addr_hit[3] & reg_we & !reg_error;
assign conf_deburst_wd = reg_wdata[1];
assign conf_serialize_we = addr_hit[3] & reg_we & !reg_error;
assign conf_serialize_wd = reg_wdata[2];
assign status_re = addr_hit[4] & reg_re & !reg_error;
assign next_id_re = addr_hit[5] & reg_re & !reg_error;
assign done_re = addr_hit[6] & reg_re & !reg_error;
// Read data return
always_comb begin
reg_rdata_next = '0;
unique case (1'b1)
addr_hit[0]: begin
reg_rdata_next[31:0] = src_addr_qs;
end
addr_hit[1]: begin
reg_rdata_next[31:0] = dst_addr_qs;
end
addr_hit[2]: begin
reg_rdata_next[31:0] = num_bytes_qs;
end
addr_hit[3]: begin
reg_rdata_next[0] = conf_decouple_qs;
reg_rdata_next[1] = conf_deburst_qs;
reg_rdata_next[2] = conf_serialize_qs;
end
addr_hit[4]: begin
reg_rdata_next[0] = status_qs;
end
addr_hit[5]: begin
reg_rdata_next[31:0] = next_id_qs;
end
addr_hit[6]: begin
reg_rdata_next[31:0] = done_qs;
end
default: begin
reg_rdata_next = '1;
end
endcase
end
// Unused signal tieoff
// wdata / byte enable are not always fully used
// add a blanket unused statement to handle lint waivers
logic unused_wdata;
logic unused_be;
assign unused_wdata = ^reg_wdata;
assign unused_be = ^reg_be;
// Assertions for Register Interface
`ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit))
endmodule
// Copyright 2022 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Samuel Riedel <sriedel@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
module idma_distributed_midend #(
/// Number of backends to distribute the requests to
parameter int unsigned NoMstPorts = 1,
/// Bytes covered by each port
parameter int unsigned DmaRegionWidth = 1, // [B] Region that one port covers in bytes
/// Start of the distributed memory region
parameter int unsigned DmaRegionStart = 32'h0000_0000,
/// End of the distributed memory region
parameter int unsigned DmaRegionEnd = 32'h1000_0000,
/// Number of generic 1D requests that can be buffered
parameter int unsigned TransFifoDepth = 1,
/// Arbitrary 1D burst request definition
parameter type burst_req_t = logic,
/// Meta data response definition
parameter type meta_t = logic
) (
input logic clk_i,
input logic rst_ni,
// Slave
input burst_req_t burst_req_i,
input logic valid_i,
output logic ready_o,
output meta_t meta_o,
// Master
output burst_req_t [NoMstPorts-1:0] burst_req_o,
output logic [NoMstPorts-1:0] valid_o,
input logic [NoMstPorts-1:0] ready_i,
input meta_t [NoMstPorts-1:0] meta_i
);
localparam DmaRegionAddressBits = $clog2(DmaRegionWidth);
localparam FullRegionAddressBits = $clog2(DmaRegionWidth*NoMstPorts);
typedef logic [FullRegionAddressBits:0] full_addr_t;
// Handle the ready signal
logic fork_ready, fifo_ready;
logic [NoMstPorts-1:0] fifo_full;
// Handle Metadata
logic [NoMstPorts-1:0] trans_complete_d, trans_complete_q;
logic [NoMstPorts-1:0] tie_off_trans_complete_d, tie_off_trans_complete_q;
logic [NoMstPorts-1:0] backend_idle_d, backend_idle_q;
assign meta_o.trans_complete = &trans_complete_q;
assign meta_o.backend_idle = &backend_idle_q;
assign fifo_ready = !(|fifo_full);
assign ready_o = fork_ready && fifo_ready;
for (genvar i = 0; unsigned'(i) < NoMstPorts; i++) begin: gen_trans_complete_fifo
// Collect the `trans_complete` signals and reduce them once we have all of them
logic empty;
logic data;
fifo_v3 #(
.FALL_THROUGH (0 ),
.DATA_WIDTH (1 ),
.DEPTH (TransFifoDepth)
) i_trans_complete_fifo (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i ('0 ),
.testmode_i ('0 ),
.full_o (fifo_full[i] ),
.empty_o (empty ),
.usage_o (/*unused*/ ),
.data_i (1'b1 ),
.push_i (trans_complete_d[i] ),
.data_o (data ),
.pop_i (meta_o.trans_complete)
);
assign trans_complete_d[i] = meta_i[i].trans_complete | tie_off_trans_complete_q[i];
assign trans_complete_q[i] = data && !empty;
end
always_comb begin
backend_idle_d = backend_idle_q;
for (int unsigned i = 0; i < NoMstPorts; i++) begin
backend_idle_d[i] = meta_i[i].backend_idle;
end
end
`FF(tie_off_trans_complete_q, tie_off_trans_complete_d, '0, clk_i, rst_ni)
`FF(backend_idle_q, backend_idle_d, '1, clk_i, rst_ni)
// Fork
logic [NoMstPorts-1:0] valid, ready;
stream_fork #(
.N_OUP (NoMstPorts)
) i_stream_fork (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.valid_i (valid_i & fifo_ready),
.ready_o (fork_ready ),
.valid_o (valid ),
.ready_i (ready )
);
full_addr_t src_addr, dst_addr, start_addr, end_addr;
assign src_addr = burst_req_i.src[FullRegionAddressBits-1:0];
assign dst_addr = burst_req_i.dst[FullRegionAddressBits-1:0];
always_comb begin
if (($unsigned(burst_req_i.src) >= DmaRegionStart) && ($unsigned(burst_req_i.src) < DmaRegionEnd)) begin
start_addr = src_addr;
end else begin
start_addr = dst_addr;
end
end_addr = start_addr+burst_req_i.num_bytes;
// Connect valid ready by default
valid_o = valid;
ready = ready_i;
// Do not interfere with metadata per default
tie_off_trans_complete_d = '0;
for (int i = 0; i < NoMstPorts; i++) begin
// Feed metadata through directly
burst_req_o[i] = burst_req_i;
// Feed through the address bits
burst_req_o[i].src = burst_req_i.src;
burst_req_o[i].dst = burst_req_i.dst;
// Modify lower addresses bits and size
if (($unsigned(start_addr) >= (i+1)*DmaRegionWidth) || ($unsigned(end_addr) <= i*DmaRegionWidth)) begin
// We are not involved in the transfer
burst_req_o[i].src = '0;
burst_req_o[i].dst = '0;
burst_req_o[i].num_bytes = 1;
// Make handshake ourselves
valid_o[i] = 1'b0;
ready[i] = 1'b1;
// Inject trans complete
if (valid) begin
tie_off_trans_complete_d[i] = 1'b1;
end
end else if (($unsigned(start_addr) >= i*DmaRegionWidth)) begin
// First (and potentially only) slice
// Leave address as is
if ($unsigned(end_addr) <= (i+1)*DmaRegionWidth) begin
burst_req_o[i].num_bytes = burst_req_i.num_bytes;
end else begin
burst_req_o[i].num_bytes = DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
end
// end else if (($unsigned(start_addr) < i*DmaRegionWidth)) begin
end else begin
// Round up the address to the next DMA boundary
if (($unsigned(burst_req_i.src) >= DmaRegionStart) && ($unsigned(burst_req_i.src) < DmaRegionEnd)) begin
burst_req_o[i].src[FullRegionAddressBits-1:0] = i*DmaRegionWidth;
burst_req_o[i].dst = burst_req_i.dst+i*DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
end else begin
burst_req_o[i].src = burst_req_i.src+i*DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
burst_req_o[i].dst[FullRegionAddressBits-1:0] = i*DmaRegionWidth;
end
if ($unsigned(end_addr) >= (i+1)*DmaRegionWidth) begin
// Middle slice
// Emit a full-sized transfer
burst_req_o[i].num_bytes = DmaRegionWidth;
end else begin
// Last slice
burst_req_o[i].num_bytes = end_addr[DmaRegionAddressBits-1:0];
end
end
end
end
// pragma translate_off
int f;
always_ff @(posedge clk_i or negedge rst_ni) begin
automatic string str;
if (rst_ni && valid_i && ready_o) begin
str = "[idma_distributed_midend] Got request\n";
str = $sformatf("%sRequest in: From: 0x%8x To: 0x%8x with size %d\n", str, burst_req_i.src, burst_req_i.dst, burst_req_i.num_bytes);
for (int i = 0; i < NoMstPorts; i++) begin
str = $sformatf("%sOut %6d: From: 0x%8x To: 0x%8x with size %d\n", str, i, burst_req_o[i].src, burst_req_o[i].dst, burst_req_o[i].num_bytes);
end
f = $fopen("dma.log", "a");
$fwrite(f, str);
$fclose(f);
end
end
// pragma translate_on
endmodule
// Copyright 2022 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Samuel Riedel <sriedel@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
module idma_split_midend #(
parameter int unsigned DmaRegionWidth = 1, // [B] Region that one port covers in bytes
parameter int unsigned DmaRegionStart = 32'h0000_0000,
parameter int unsigned DmaRegionEnd = 32'h1000_0000,
parameter int unsigned AddrWidth = 32,
parameter type burst_req_t = logic,
parameter type meta_t = logic
) (
input logic clk_i,
input logic rst_ni,
// Slave
input burst_req_t burst_req_i,
input logic valid_i,
output logic ready_o,
output meta_t meta_o,
// Master
output burst_req_t burst_req_o,
output logic valid_o,
input logic ready_i,
input meta_t meta_i
);
localparam DmaRegionAddressBits = $clog2(DmaRegionWidth);
typedef logic [AddrWidth-1:0] addr_t;
addr_t start_addr, end_addr;
logic req_valid;
// Handle Metadata
// Forward idle signal and count the trans_comlete signal
logic [31:0] num_trans_d, num_trans_q;
assign meta_o.backend_idle = meta_i.backend_idle;
always_comb begin
num_trans_d = num_trans_q;
meta_o.trans_complete = 1'b0;
if (req_valid) begin
num_trans_d += 1;
end
if (meta_i.trans_complete) begin
num_trans_d -= 1;
end
if (num_trans_q == 1 && num_trans_d == 0) begin
meta_o.trans_complete = 1'b1;
end
end
`FF(num_trans_q, num_trans_d, '0, clk_i, rst_ni)
// Split requests
always_comb begin
if (($unsigned(burst_req_i.src) >= DmaRegionStart) && ($unsigned(burst_req_i.src) < DmaRegionEnd)) begin
start_addr = burst_req_i.src;
end else begin
start_addr = burst_req_i.dst;
end
end_addr = start_addr + burst_req_i.num_bytes;
end
enum logic {Idle, Busy} state_d, state_q;
burst_req_t req_d, req_q;
`FFARN(state_q, state_d, Idle, clk_i, rst_ni)
`FFARN(req_q, req_d, '0, clk_i, rst_ni)
always_comb begin
state_d = state_q;
req_d = req_q;
burst_req_o = '0;
valid_o = 1'b0;
ready_o = 1'b0;
req_valid = 1'b0;
unique case (state_q)
Idle: begin
if (valid_i) begin // Splitting required.
if (DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0] >= burst_req_i.num_bytes) begin
// No splitting required, just forward
burst_req_o = burst_req_i;
valid_o = 1'b1;
ready_o = ready_i;
req_valid = ready_i;
end else begin
// Store and acknowledge
req_d = burst_req_i;
ready_o = 1'b1;
// Feed through the first request
burst_req_o = burst_req_i;
// Modify it's size
burst_req_o.num_bytes = DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
// Forward request
valid_o = 1'b1;
if (ready_i) begin
// Increment the address and reduce the number of outstanding splits
req_d.num_bytes -= DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
req_d.src += DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
req_d.dst += DmaRegionWidth-start_addr[DmaRegionAddressBits-1:0];
req_valid = 1'b1;
end
state_d = Busy;
end
end
end
Busy: begin
// Sent next burst from split.
burst_req_o = req_q;
valid_o = 1'b1;
req_valid = ready_i;
if (req_q.num_bytes <= DmaRegionWidth) begin
// Last split
if (ready_i) begin
state_d = Idle;
end
end else begin
// Clip size and increment address
burst_req_o.num_bytes = DmaRegionWidth;
if (ready_i) begin
req_d.num_bytes = req_q.num_bytes - DmaRegionWidth;
req_d.src = req_q.src + DmaRegionWidth;
req_d.dst = req_q.dst + DmaRegionWidth;
end
end
end
default: /*do nothing*/;
endcase
end
// pragma translate_off
int f;
always_ff @(posedge clk_i or negedge rst_ni) begin
automatic string str;
if (rst_ni && valid_i && ready_o) begin
str = "[idma_split_midend] Got request\n";
str = $sformatf("%sSplit: Request in: From: 0x%8x To: 0x%8x with size %d\n", str, burst_req_i.src, burst_req_i.dst, burst_req_i.num_bytes);
f = $fopen("dma.log", "a");
$fwrite(f, str);
$fclose(f);
end
if (rst_ni && valid_o && ready_i) begin
str = $sformatf("Split: Out %6d: From: 0x%8x To: 0x%8x with size %d\n", num_trans_q, burst_req_o.src, burst_req_o.dst, burst_req_o.num_bytes);
f = $fopen("dma.log", "a");
$fwrite(f, str);
$fclose(f);
end
end
// pragma translate_on
endmodule
diff --git a/hardware/deps/register_interface/include/register_interface/assign.svh b/hardware/deps/register_interface/include/register_interface/assign.svh
index 30c44ac..267dfab 100644
--- a/hardware/deps/register_interface/include/register_interface/assign.svh
+++ b/hardware/deps/register_interface/include/register_interface/assign.svh
@@ -43,4 +43,4 @@
assign lhs.error = rhs.error; \
assign lhs.ready = rhs.ready;
-`endif
\ No newline at end of file
+`endif
diff --git a/hardware/deps/tech_cells_generic/Bender.yml b/hardware/deps/tech_cells_generic/Bender.yml
index 4efe945..386cf62 100644
--- a/hardware/deps/tech_cells_generic/Bender.yml
+++ b/hardware/deps/tech_cells_generic/Bender.yml
@@ -10,7 +10,7 @@ sources:
- src/deprecated/cluster_clk_cells.sv
- src/deprecated/pulp_clk_cells.sv
- - target: all(rtl, not(synthesis))
+ - target: any(all(rtl, not(synthesis)), verilator)
files:
# level 0
- src/rtl/tc_sram.sv
@@ -22,7 +22,7 @@ sources:
- src/fpga/tc_clk_xilinx.sv
- src/fpga/tc_sram_xilinx.sv
- - target: all(not(all(fpga, xilinx)), not(synthesis))
+ - target: any(all(not(all(fpga, xilinx)), not(synthesis)), verilator)
files:
# Level 0
- src/rtl/tc_clk.sv
diff --git a/hardware/deps/tech_cells_generic/src/deprecated/pulp_clk_cells.sv b/hardware/deps/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
index 53ad07f..4ddd4e1 100644
--- a/hardware/deps/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
+++ b/hardware/deps/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
@@ -94,6 +94,7 @@ module pulp_clock_xor2 (
endmodule
`ifndef SYNTHESIS
+`ifndef VERILATOR
module pulp_clock_delay(
input logic in_i,
output logic out_o
@@ -103,5 +104,6 @@ module pulp_clock_delay(
endmodule
`endif
+`endif
diff --git a/hardware/deps/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv b/hardware/deps/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv
index 5c95b55..88e9707 100644
--- a/hardware/deps/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv
+++ b/hardware/deps/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv
@@ -41,4 +41,4 @@ module pulp_clock_gating_async #(
.clk_o
);
-endmodule
\ No newline at end of file
+endmodule
diff --git a/hardware/deps/tech_cells_generic/src/rtl/tc_sram.sv b/hardware/deps/tech_cells_generic/src/rtl/tc_sram.sv
index 53530e0..1e5ff2d 100644
--- a/hardware/deps/tech_cells_generic/src/rtl/tc_sram.sv
+++ b/hardware/deps/tech_cells_generic/src/rtl/tc_sram.sv
@@ -124,9 +124,11 @@ module tc_sram #(
// write memory array
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
+ `ifndef VERILATOR
for (int unsigned i = 0; i < NumWords; i++) begin
sram[i] <= init_val[i];
end
+ `endif
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
// initialize the read output register for each port
@@ -149,12 +151,14 @@ module tc_sram #(
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
+ `ifndef VERILATOR
// update value when write is set at clock
for (int unsigned j = 0; j < DataWidth; j++) begin
if (be_i[i][j/ByteWidth]) begin
sram[addr_i[i]][j] <= wdata_i[i][j];
end
end
+ `endif
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
@@ -164,6 +168,23 @@ module tc_sram #(
end // if !rst_ni
end
+ `ifdef VERILATOR
+ for (genvar i = 0; i < NumPorts; i++) begin
+ // update value when write is set at clock
+ for (genvar j = 0; j < DataWidth; j++) begin
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ end else begin
+ if (req_i[i])
+ if (we_i[i])
+ if (be_i[i][j/ByteWidth])
+ sram[addr_i[i]][j] <= wdata_i[i][j];
+ end
+ end
+ end
+ end
+ `endif
+
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
@@ -204,4 +225,69 @@ module tc_sram #(
`endif
`endif
// pragma translate_on
+
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/**
+ * Memory loader for simulation
+ *
+ * Include this file in a memory primitive to load a memory array from
+ * simulation.
+ *
+ * Requirements:
+ * - A memory array named `sram`.
+ * - A parameter `DataWidth` giving the memory width (word size) in bit.
+ * - A parameter `NumWords` giving the memory depth in words.
+ */
+
+`ifndef SYNTHESIS
+ // Task for loading 'sram' with SystemVerilog system task $readmemh()
+ export "DPI-C" task simutil_memload;
+
+ task simutil_memload;
+ input string file;
+ $readmemh(file, sram);
+ endtask
+
+ // Function for setting a specific element in |sram|
+ // Returns 1 (true) for success, 0 (false) for errors.
+ export "DPI-C" function simutil_set_mem;
+
+ function int simutil_set_mem(input int index, input bit [1023:0] val);
+
+ // Function will only work for memories <= 1024 bits
+ if (DataWidth > 1024) begin
+ return 0;
+ end
+
+ if (index >= NumWords) begin
+ return 0;
+ end
+
+ sram[index] = val[DataWidth-1:0];
+ return 1;
+ endfunction
+
+ // Function for getting a specific element in |sram|
+ export "DPI-C" function simutil_get_mem;
+
+ function int simutil_get_mem(input int index, output bit [1023:0] val);
+
+ // Function will only work for memories <= 1024 bits
+ if (DataWidth > 1024) begin
+ return 0;
+ end
+
+ if (index >= NumWords) begin
+ return 0;
+ end
+
+ val = 0;
+ val[DataWidth-1:0] = sram[index];
+ return 1;
+ endfunction
+`endif
+
endmodule
compile.tcl
transcript
*.log
*.lock
work
Bender.lock
# Copyright 2020 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
package:
name: reqrsp_interface
authors:
- Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
- Florian Zaruba <zarubaf@iis.ee.ethz.ch>
dependencies:
axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.29.2 }
common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.23.0 }
export_include_dirs:
- include
sources:
# Level 0:
- src/reqrsp_pkg.sv
# Level 1:
- src/reqrsp_intf.sv
# Level 2:
- src/axi_to_reqrsp.sv
- src/reqrsp_cut.sv
- src/reqrsp_demux.sv
- src/reqrsp_iso.sv
- src/reqrsp_mux.sv
- src/reqrsp_to_axi.sv
- target: simulation
files:
- src/reqrsp_test.sv
- target: test
files:
# Level 0
- test/axi_to_reqrsp_tb.sv
- test/reqrsp_demux_tb.sv
- test/reqrsp_idempotent_tb.sv
- test/reqrsp_mux_tb.sv
- test/reqrsp_to_axi_tb.sv
# Reqrsp Interface
A simple interface with a single request channel and a single response channel,
for both read and write data. A more thorough documentation can be found in the
[docs](doc/index.md) folder.
## List of Modules
| Name | Description | Status |
| ---------------- | ---------------------------------------------------------------------------- | ---------- |
| `axi_to_reqrsp` | Protocol translator between `reqrsp` and AXI4+ATOP | active |
| `reqrsp_cut` | Insert registers (i.e., a pipeline stage) into request and/or response path. | active |
| `reqrsp_demux` | Demultiplex, based on a select signal. | active |
| `reqrsp_intf` | Systemverilog interface definition of `reqrsp` interface. | active |
| `reqrsp_iso` | Isochronous clockdomain crossing. | active |
| `reqrsp_mux` | Arbitrate multiple ports based on round-robin arbitration. | active |
| `reqrsp_test` | Common test infrastructure for the `reqrsp` protocol. | active |
| `reqrsp_to_axi` | Protocol translator between AXI4+ATOP and `reqrsp`. | active |
| `reqrsp_to_tcdm` | Protocol translator between `reqrsp` and `tcdm` protocol. | _untested_ |
# Reqrsp Interface
The `reqrsp_interface` (request and response) is a custom interface based on
common principles found in other interconnects such as AXI or TileLink. It has
only two channels (request and response) which are handshaked according to the
AMBA standard:
- The initiator asserts `valid`. The assertion of `valid` must not depend on
`ready`.
- Once `valid` has been asserted all data must remain stable.
- The receiver asserts `ready` whenever it is ready to receive the transaction.
- When both `valid` and `ready` are high the transaction is successful.
The bus is little endian.
## Channels
The two channels are:
- Request (`q`): The initiator requests a memory transaction. Supported are
read, write, atomic memory operations, load-linked and store-conditional
pairs.
- Response (`p`): _Every_ transaction which was requested ultimately returns a
response. For reads the response channel returns the read response, for writes
the response acknowledges that the data is committed and subsequent reads will
return the last written value, for atomic operations the data _before_ the
memory operations has happened is being returned. For load-linked the the read
response is returned, for store-conditionals the the success code is returned.
Additionally, the response channel carries error information.
## Sizes
Data bus size of 32, 64, 128, 256, 512, 1024-bit are supported. Atomic memory
operations are 32 bit or 64 bit for bus sizes greater than 32.
## Data Alignment
The data is always bus-aligned. similar to AXI. For reads the address and size
fields indicate which data is valid. Similarly for writes the address and size
fields in addition to the write strobe field indicate valid bytes (8 bit of
data). Atomics and LR/SCs addresses are always naturally aligned to their size.
## Ordering
Transactions on the request channel are always strongly ordered (as if they
would all use the same ID in AXI terms). Reads and writes are ordered with
respect to each other (that needs to be maintained when translating to AXI).
## Atomic Memory Operations
Atomic memory operations supported are:
| Operation | Description |
| ------------------ | ------------------------------------------------------------------------- |
| `Swap` | Swaps the values. |
| `Add` | Signed addition. |
| `And`, `Or`, `Xor` | Bitwise `and`, `or`, and `xor` operation. |
| `Max`, `Maxu` | Signed and unsigned maximum operation. |
| `Min`, `Minu` | Signed and unsigned minimum operation. |
| `Lr` | Places a reservation on the given memory address |
| `Sc` | Conditional store, returns `0` on `q.data` if successful, `1` otherwise. |
The operation reads the value at the given address and returns the read value on
the `p` channel. The same memory location is updated with the data and operation
supplied on the `q.amo` and `q.data` signals. The `q.write` signal must be set to `1'b0`.
## Load-Reserved/Store-conditional
The `q.amo` field carries the information on whether the transaction encodes an
LR/SC.
| Operation | Description |
| ------------ | ------------------------------------------------------------------------- |
| Lr | Places a reservation on the given memory address |
| Sc | Conditional store, returns `0` on `q.data` if successful, `1` otherwise. |
For `Sc` and `LR` the `q.write` signal must be set to `0`.
## Error
The `p` channel carries additional error information:
- `0`: The access is ok.
- `1`: The access has encountered an error.
// 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>
// Author: Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// Macros to assign reqrsp Interfaces and Structs
`ifndef REQRSP_ASSIGN_SVH_
`define REQRSP_ASSIGN_SVH_
// Assign an reqrsp handshake.
`define REQRSP_ASSIGN_VALID(__opt_as, __dst, __src, __chan) \
__opt_as ``__dst``.``__chan``_valid = ``__src``.``__chan``_valid;
`define REQRSP_ASSIGN_READY(__opt_as, __dst, __src, __chan) \
__opt_as ``__dst``.``__chan``_ready = ``__src``.``__chan``_ready;
`define REQRSP_ASSIGN_HANDSHAKE(__opt_as, __dst, __src, __chan) \
`REQRSP_ASSIGN_VALID(__opt_as, __dst, __src, __chan) \
`REQRSP_ASSIGN_READY(__opt_as, __src, __dst, __chan)
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning one REQRSP interface to another, as if you would do `assign slv =
// mst;`
//
// The channel assignments `REQRSP_ASSIGN_XX(dst, src)` assign all payload and
// the valid signal of the `XX` channel from the `src` to the `dst` interface
// and they assign the ready signal from the `src` to the `dst` interface. The
// interface assignment `REQRSP_ASSIGN(dst, src)` assigns all channels including
// handshakes as if `src` was the master of `dst`.
//
// Usage Example: `REQRSP_ASSIGN(slv, mst) `REQRSP_ASSIGN_Q(dst, src, aw)
// `REQRSP_ASSIGN_P(dst, src)
`define REQRSP_ASSIGN_Q_CHAN(__opt_as, dst, src, __sep_dst, __sep_src) \
__opt_as dst.q``__sep_dst``addr = src.q``__sep_src``addr; \
__opt_as dst.q``__sep_dst``write = src.q``__sep_src``write; \
__opt_as dst.q``__sep_dst``amo = src.q``__sep_src``amo; \
__opt_as dst.q``__sep_dst``data = src.q``__sep_src``data; \
__opt_as dst.q``__sep_dst``strb = src.q``__sep_src``strb; \
__opt_as dst.q``__sep_dst``size = src.q``__sep_src``size;
`define REQRSP_ASSIGN_P_CHAN(__opt_as, dst, src, __sep_dst, __sep_src) \
__opt_as dst.p``__sep_dst``data = src.p``__sep_src``data; \
__opt_as dst.p``__sep_dst``error = src.p``__sep_src``error;
`define REQRSP_ASSIGN(slv, mst) \
`REQRSP_ASSIGN_Q_CHAN(assign, slv, mst, _, _) \
`REQRSP_ASSIGN_HANDSHAKE(assign, slv, mst, q) \
`REQRSP_ASSIGN_P_CHAN(assign, mst, slv, _, _) \
`REQRSP_ASSIGN_HANDSHAKE(assign, mst, slv, p)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning an interface from channel or request/response structs outside a
// process.
//
// The request macro `REQRSP_ASSIGN_FROM_REQ(reqrsp_if, req_struct)` assigns the
// request channel and the request-side handshake signals of the `reqrsp_if`
// interface from the signals in `req_struct`. The response macro
// `REQRSP_ASSIGN_FROM_RESP(reqrsp_if, resp_struct)` assigns the response
// channel and the response-side handshake signals of the `reqrsp_if` interface
// from the signals in `resp_struct`.
//
// Usage Example:
// `REQRSP_ASSIGN_FROM_REQ(my_if, my_req_struct)
`define REQRSP_ASSIGN_FROM_REQ(reqrsp_if, req_struct) \
`REQRSP_ASSIGN_VALID(assign, reqrsp_if, req_struct, q) \
`REQRSP_ASSIGN_Q_CHAN(assign, reqrsp_if, req_struct, _, .) \
`REQRSP_ASSIGN_READY(assign, reqrsp_if, req_struct, p)
`define REQRSP_ASSIGN_FROM_RESP(reqrsp_if, resp_struct) \
`REQRSP_ASSIGN_READY(assign, reqrsp_if, resp_struct, q) \
`REQRSP_ASSIGN_P_CHAN(assign, reqrsp_if, resp_struct, _, .) \
`REQRSP_ASSIGN_VALID(assign, reqrsp_if, resp_struct, p)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from an interface outside a
// process.
//
// The request macro `REQRSP_ASSIGN_TO_REQ(reqrsp_if, req_struct)` assigns all
// signals of `req_struct` payload and request-side handshake signals to the
// signals in the `reqrsp_if` interface. The response macro
// `REQRSP_ASSIGN_TO_RESP(reqrsp_if, resp_struct)` assigns all signals of
// `resp_struct` payload and response-side handshake signals to the signals in
// the `reqrsp_if` interface.
//
// Usage Example:
// `REQRSP_ASSIGN_TO_REQ(my_req_struct, my_if)
`define REQRSP_ASSIGN_TO_REQ(req_struct, reqrsp_if) \
`REQRSP_ASSIGN_VALID(assign, req_struct, reqrsp_if, q) \
`REQRSP_ASSIGN_Q_CHAN(assign, req_struct, reqrsp_if, ., _) \
`REQRSP_ASSIGN_READY(assign, req_struct, reqrsp_if, p)
`define REQRSP_ASSIGN_TO_RESP(resp_struct, reqrsp_if) \
`REQRSP_ASSIGN_READY(assign, resp_struct, reqrsp_if, q) \
`REQRSP_ASSIGN_P_CHAN(assign, resp_struct, reqrsp_if, ., _) \
`REQRSP_ASSIGN_VALID(assign, resp_struct, reqrsp_if, p)
////////////////////////////////////////////////////////////////////////////////////////////////////
`endif
// 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>
// Author: Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
`ifndef REQRSP_INTERFACE_TYPEDEF_SVH_
`define REQRSP_INTERFACE_TYPEDEF_SVH_
`define REQRSP_TYPEDEF_REQ_CHAN_T(__req_chan_t, __addr_t, __data_t, __strb_t) \
typedef struct packed { \
__addr_t addr; \
logic write; \
reqrsp_pkg::amo_op_e amo; \
__data_t data; \
__strb_t strb; \
reqrsp_pkg::size_t size; \
} __req_chan_t;
`define REQRSP_TYPEDEF_RSP_CHAN_T(__rsp_chan_t, __data_t) \
typedef struct packed { \
__data_t data; \
logic error; \
} __rsp_chan_t;
`define REQRSP_TYPEDEF_REQ_T(__req_t, __req_chan_t) \
typedef struct packed { \
__req_chan_t q; \
logic q_valid; \
logic p_ready; \
} __req_t;
`define REQRSP_TYPEDEF_RSP_T(__rsp_t, __rsp_chan_t) \
typedef struct packed { \
__rsp_chan_t p; \
logic p_valid; \
logic q_ready; \
} __rsp_t;
`define REQRSP_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \
`REQRSP_TYPEDEF_REQ_CHAN_T(__name``_req_chan_t, __addr_t, __data_t, __strb_t) \
`REQRSP_TYPEDEF_RSP_CHAN_T(__name``_rsp_chan_t, __data_t) \
`REQRSP_TYPEDEF_REQ_T(__name``_req_t, __name``_req_chan_t) \
`REQRSP_TYPEDEF_RSP_T(__name``_rsp_t, __name``_rsp_chan_t)
`endif
// 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 "reqrsp_interface/typedef.svh"
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
/// Cut all combinatorial paths through the `reqrsp` interface.
module reqrsp_cut #(
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Request type.
parameter type req_t = logic,
/// Response type.
parameter type rsp_t = logic,
/// Bypass request channel.
parameter bit BypassReq = 0,
/// Bypass Response channel.
parameter bit BypassRsp = 0
) (
input logic clk_i,
input logic rst_ni,
input req_t slv_req_i,
output rsp_t slv_rsp_o,
output req_t mst_req_o,
input rsp_t mst_rsp_i
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t)
spill_register #(
.T (reqrsp_req_chan_t),
.Bypass ( BypassReq )
) i_spill_register_q (
.clk_i,
.rst_ni,
.valid_i (slv_req_i.q_valid) ,
.ready_o (slv_rsp_o.q_ready) ,
.data_i (slv_req_i.q),
.valid_o (mst_req_o.q_valid),
.ready_i (mst_rsp_i.q_ready),
.data_o (mst_req_o.q)
);
spill_register #(
.T (reqrsp_rsp_chan_t),
.Bypass ( BypassRsp )
) i_spill_register_p (
.clk_i,
.rst_ni,
.valid_i (mst_rsp_i.p_valid) ,
.ready_o (mst_req_o.p_ready) ,
.data_i (mst_rsp_i.p),
.valid_o (slv_rsp_o.p_valid),
.ready_i (slv_req_i.p_ready),
.data_o (slv_rsp_o.p)
);
endmodule
`include "reqrsp_interface/typedef.svh"
`include "reqrsp_interface/assign.svh"
module reqrsp_cut_intf #(
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Bypass request channel.
parameter bit BypassReq = 0,
/// Bypass Response channel.
parameter bit BypassRsp = 0
) (
input logic clk_i,
input logic rst_ni,
REQRSP_BUS slv,
REQRSP_BUS mst
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t)
reqrsp_req_t reqrsp_slv_req, reqrsp_mst_req;
reqrsp_rsp_t reqrsp_slv_rsp, reqrsp_mst_rsp;
reqrsp_cut #(
.AddrWidth (AddrWidth),
.DataWidth (DataWidth),
.req_t (reqrsp_req_t),
.rsp_t (reqrsp_rsp_t),
.BypassReq (BypassReq),
.BypassRsp (BypassRsp)
) i_reqrsp_cut (
.clk_i,
.rst_ni,
.slv_req_i (reqrsp_slv_req),
.slv_rsp_o (reqrsp_slv_rsp),
.mst_req_o (reqrsp_mst_req),
.mst_rsp_i (reqrsp_mst_rsp)
);
`REQRSP_ASSIGN_TO_REQ(reqrsp_slv_req, slv)
`REQRSP_ASSIGN_FROM_RESP(slv, reqrsp_slv_rsp)
`REQRSP_ASSIGN_FROM_REQ(mst, reqrsp_mst_req)
`REQRSP_ASSIGN_TO_RESP(reqrsp_mst_rsp, mst)
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>
`include "reqrsp_interface/typedef.svh"
`include "common_cells/assertions.svh"
module reqrsp_demux #(
/// Number of input ports.
parameter int unsigned NrPorts = 2,
/// Request type.
parameter type req_t = logic,
/// Response type.
parameter type rsp_t = logic,
/// Amount of outstanding responses. Determines the FIFO size.
parameter int unsigned RespDepth = 8,
// Dependent parameters, DO NOT OVERRIDE!
parameter int unsigned SelectWidth = cf_math_pkg::idx_width(NrPorts),
parameter type select_t = logic [SelectWidth-1:0]
) (
input logic clk_i,
input logic rst_ni,
input select_t slv_select_i,
input req_t slv_req_i,
output rsp_t slv_rsp_o,
output req_t [NrPorts-1:0] mst_req_o,
input rsp_t [NrPorts-1:0] mst_rsp_i
);
logic [NrPorts-1:0] fwd;
logic req_ready, req_valid;
logic fifo_full, fifo_empty;
logic [NrPorts-1:0] fifo_data;
logic push_id_fifo, pop_id_fifo;
// we need space in the return id fifo, silence if no space is available
assign req_valid = ~fifo_full & slv_req_i.q_valid;
assign slv_rsp_o.q_ready = ~fifo_full & req_ready;
// Stream demux (unwrapped because of struct issues)
always_comb begin
for (int i = 0; i < NrPorts; i++) begin
mst_req_o[i].q_valid = '0;
mst_req_o[i].q = slv_req_i.q;
mst_req_o[i].p_ready = fwd[i] ? slv_req_i.p_ready : 1'b0;
end
mst_req_o[slv_select_i].q_valid = req_valid;
end
assign req_ready = mst_rsp_i[slv_select_i].q_ready;
assign push_id_fifo = req_valid & slv_rsp_o.q_ready;
assign pop_id_fifo = slv_rsp_o.p_valid & slv_req_i.p_ready;
for (genvar i = 0; i < NrPorts; i++) begin : gen_fifo_data
assign fifo_data[i] = mst_rsp_i[i].q_ready & mst_req_o[i].q_valid;
end
// Remember selected master for correct forwarding of read data/acknowledge.
fifo_v3 #(
.DATA_WIDTH ( NrPorts ),
.DEPTH ( RespDepth )
) i_id_fifo (
.clk_i,
.rst_ni,
.flush_i (1'b0),
.testmode_i (1'b0),
.full_o (fifo_full),
.empty_o (fifo_empty),
.usage_o ( ),
// Onehot mask.
.data_i (fifo_data),
.push_i (push_id_fifo),
.data_o (fwd),
.pop_i (pop_id_fifo)
);
// Response data routing.
always_comb begin
slv_rsp_o.p = '0;
slv_rsp_o.p_valid = '0;
for (int i = 0; i < NrPorts; i++) begin
if (fwd[i]) begin
slv_rsp_o.p = mst_rsp_i[i].p;
slv_rsp_o.p_valid = mst_rsp_i[i].p_valid;
end
end
end
`ASSERT(SelectStable, slv_req_i.q_valid && !slv_rsp_o.q_ready |=> $stable(slv_select_i))
`ASSERT(SelectInBounds, slv_req_i.q_valid |-> (slv_select_i <= NrPorts))
endmodule
`include "reqrsp_interface/typedef.svh"
`include "reqrsp_interface/assign.svh"
/// Reqrsp demux interface version.
module reqrsp_demux_intf #(
/// Number of input ports.
parameter int unsigned NrPorts = 2,
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Amount of outstanding responses. Determines the FIFO size.
parameter int unsigned RespDepth = 8,
// Dependent parameters, DO NOT OVERRIDE!
parameter int unsigned SelectWidth = cf_math_pkg::idx_width(NrPorts),
parameter type select_t = logic [SelectWidth-1:0]
) (
input logic clk_i,
input logic rst_ni,
input select_t slv_select_i,
REQRSP_BUS slv,
REQRSP_BUS mst [NrPorts]
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t)
reqrsp_req_t reqrsp_slv_req;
reqrsp_rsp_t reqrsp_slv_rsp;
reqrsp_req_t [NrPorts-1:0] reqrsp_mst_req;
reqrsp_rsp_t [NrPorts-1:0] reqrsp_mst_rsp;
reqrsp_demux #(
.NrPorts (NrPorts),
.req_t (reqrsp_req_t),
.rsp_t (reqrsp_rsp_t),
.RespDepth (RespDepth)
) i_reqrsp_demux (
.clk_i,
.rst_ni,
.slv_select_i (slv_select_i),
.slv_req_i (reqrsp_slv_req),
.slv_rsp_o (reqrsp_slv_rsp),
.mst_req_o (reqrsp_mst_req),
.mst_rsp_i (reqrsp_mst_rsp)
);
`REQRSP_ASSIGN_TO_REQ(reqrsp_slv_req, slv)
`REQRSP_ASSIGN_FROM_RESP(slv, reqrsp_slv_rsp)
for (genvar i = 0; i < NrPorts; i++) begin : gen_interface_assignment
`REQRSP_ASSIGN_FROM_REQ(mst[i], reqrsp_mst_req[i])
`REQRSP_ASSIGN_TO_RESP(reqrsp_mst_rsp[i], mst[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 simple two channel interface.
///
/// This interface provides two channels, one for requests and one for
/// responses. Both channels have a valid/ready handshake. The sender sets the
/// channel signals and pulls valid high. Once pulled high, valid must remain
/// high and none of the signals may change. The transaction completes when both
/// valid and ready are high. Valid must not depend on ready. The master can
/// request a read or write on the `q` channel. The slave responds on the `p`
/// channel once the action has been completed. Every transaction returns a
/// response. For write transaction the datum on the response channel is
/// invalid. For read and atomic transaction the value corresponds to the
/// un-modified value (the value read before an operation was performed).For
/// further details see docs/index.md
interface REQRSP_BUS #(
/// The width of the address.
parameter int ADDR_WIDTH = -1,
/// The width of the data.
parameter int DATA_WIDTH = -1
);
import reqrsp_pkg::*;
localparam int unsigned StrbWidth = DATA_WIDTH / 8;
typedef logic [ADDR_WIDTH-1:0] addr_t;
typedef logic [DATA_WIDTH-1:0] data_t;
typedef logic [StrbWidth-1:0] strb_t;
/// The request channel (Q).
addr_t q_addr;
/// 0 = read, 1 = write, 1 = amo fetch-and-op
logic q_write;
amo_op_e q_amo;
data_t q_data;
/// Byte-wise strobe
strb_t q_strb;
size_t q_size;
logic q_valid;
logic q_ready;
/// The response channel (P).
data_t p_data;
/// 0 = ok, 1 = error
logic p_error;
logic p_valid;
logic p_ready;
modport in (
input q_addr, q_write, q_amo, q_size, q_data, q_strb, q_valid, p_ready,
output q_ready, p_data, p_error, p_valid
);
modport out (
output q_addr, q_write, q_amo, q_size, q_data, q_strb, q_valid, p_ready,
input q_ready, p_data, p_error, p_valid
);
endinterface
// The DV interface additionally caries a clock signal.
interface REQRSP_BUS_DV #(
/// The width of the address.
parameter int ADDR_WIDTH = -1,
/// The width of the data.
parameter int DATA_WIDTH = -1
) (
input logic clk_i
);
import reqrsp_pkg::*;
localparam int unsigned StrbWidth = DATA_WIDTH / 8;
typedef logic [ADDR_WIDTH-1:0] addr_t;
typedef logic [DATA_WIDTH-1:0] data_t;
typedef logic [StrbWidth-1:0] strb_t;
/// The request channel (Q).
addr_t q_addr;
/// 0 = read, 1 = write, 1 = amo fetch-and-op
logic q_write;
amo_op_e q_amo;
data_t q_data;
/// Byte-wise strobe
strb_t q_strb;
size_t q_size;
logic q_valid;
logic q_ready;
/// The response channel (P).
data_t p_data;
/// 0 = ok, 1 = error
logic p_error;
logic p_valid;
logic p_ready;
modport in (
input q_addr, q_write, q_amo, q_size, q_data, q_strb, q_valid, p_ready,
output q_ready, p_data, p_error, p_valid
);
modport out (
output q_addr, q_write, q_amo, q_size, q_data, q_strb, q_valid, p_ready,
input q_ready, p_data, p_error, p_valid
);
modport monitor (
input q_addr, q_write, q_amo, q_size, q_data, q_strb, q_valid, p_ready,
q_ready, p_data, p_error, p_valid
);
// pragma translate_off
`ifndef VERILATOR
assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_addr)));
assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_write)));
assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_amo)));
assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_size)));
assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_data)));
assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_strb)));
assert property (@(posedge clk_i) (q_valid && !q_ready |=> q_valid));
assert property (@(posedge clk_i) (p_valid && !p_ready |=> $stable(p_data)));
assert property (@(posedge clk_i) (p_valid && !p_ready |=> $stable(p_error)));
assert property (@(posedge clk_i) (p_valid && !p_ready |=> p_valid));
`endif
// pragma translate_on
endinterface
// 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 "reqrsp_interface/typedef.svh"
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
/// Decouple `src` and `dst` side using an isochronous clock-domain crossing.
/// See `common_cells/isochronous_spill_register` for further detail on the
/// clock requirements.
module reqrsp_iso #(
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Request type.
parameter type req_t = logic,
/// Response type.
parameter type rsp_t = logic,
/// Bypass.
parameter bit BypassReq = 0,
parameter bit BypassRsp = 0
) (
/// Clock of source clock domain.
input logic src_clk_i,
/// Active low async reset in source domain.
input logic src_rst_ni,
/// Source request data.
input req_t src_req_i,
/// Source response data.
output rsp_t src_rsp_o,
/// Clock of destination clock domain.
input logic dst_clk_i,
/// Active low async reset in destination domain.
input logic dst_rst_ni,
/// Destination request data.
output req_t dst_req_o,
/// Destination response data.
input rsp_t dst_rsp_i
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t)
isochronous_spill_register #(
.T (reqrsp_req_chan_t),
.Bypass (BypassReq)
) i_isochronous_spill_register_q (
.src_clk_i (src_clk_i),
.src_rst_ni (src_rst_ni),
.src_valid_i (src_req_i.q_valid) ,
.src_ready_o (src_rsp_o.q_ready) ,
.src_data_i (src_req_i.q),
.dst_clk_i (dst_clk_i),
.dst_rst_ni (dst_rst_ni),
.dst_valid_o (dst_req_o.q_valid),
.dst_ready_i (dst_rsp_i.q_ready),
.dst_data_o (dst_req_o.q)
);
isochronous_spill_register #(
.T (reqrsp_rsp_chan_t),
.Bypass (BypassRsp)
) i_isochronous_spill_register_p (
.src_clk_i (dst_clk_i),
.src_rst_ni (dst_rst_ni),
.src_valid_i (dst_rsp_i.p_valid) ,
.src_ready_o (dst_req_o.p_ready) ,
.src_data_i (dst_rsp_i.p),
.dst_clk_i (src_clk_i),
.dst_rst_ni (src_rst_ni),
.dst_valid_o (src_rsp_o.p_valid),
.dst_ready_i (src_req_i.p_ready),
.dst_data_o (src_rsp_o.p)
);
endmodule
`include "reqrsp_interface/typedef.svh"
`include "reqrsp_interface/assign.svh"
/// Interface wrapper for `reqrsp_iso`.
module reqrsp_iso_intf #(
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Bypass.
parameter bit BypassReq = 0,
parameter bit BypassRsp = 0
) (
/// Clock of source clock domain.
input logic src_clk_i,
/// Active low async reset in source domain.
input logic src_rst_ni,
/// Source interface.
REQRSP_BUS src,
/// Clock of destination clock domain.
input logic dst_clk_i,
/// Active low async reset in destination domain.
input logic dst_rst_ni,
/// Destination interface.
REQRSP_BUS dst
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t)
reqrsp_req_t reqrsp_src_req, reqrsp_dst_req;
reqrsp_rsp_t reqrsp_src_rsp, reqrsp_dst_rsp;
reqrsp_iso #(
.AddrWidth (AddrWidth),
.DataWidth (DataWidth),
.req_t (reqrsp_req_t),
.rsp_t (reqrsp_rsp_t),
.BypassReq (BypassReq),
.BypassRsp (BypassRsp)
) i_reqrsp_iso (
.src_clk_i,
.src_rst_ni,
.src_req_i (reqrsp_src_req),
.src_rsp_o (reqrsp_src_rsp),
.dst_clk_i,
.dst_rst_ni,
.dst_req_o (reqrsp_dst_req),
.dst_rsp_i (reqrsp_dst_rsp)
);
`REQRSP_ASSIGN_TO_REQ(reqrsp_src_req, src)
`REQRSP_ASSIGN_FROM_RESP(src, reqrsp_src_rsp)
`REQRSP_ASSIGN_FROM_REQ(dst, reqrsp_dst_req)
`REQRSP_ASSIGN_TO_RESP(reqrsp_dst_rsp, dst)
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
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
`include "reqrsp_interface/typedef.svh"
/// Multiplex multiple `reqrsp` ports onto one, based on arbitration.
module reqrsp_mux #(
/// Number of input ports.
parameter int unsigned NrPorts = 2,
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Request type.
parameter type req_t = logic,
/// Response type.
parameter type rsp_t = logic,
/// Amount of outstanding responses. Determines the FIFO size.
parameter int unsigned RespDepth = 8,
/// Cut timing paths on the request path. Incurs a cycle additional latency.
/// Registers are inserted at the slave side.
parameter bit [NrPorts-1:0] RegisterReq = '0
) (
input logic clk_i,
input logic rst_ni,
input req_t [NrPorts-1:0] slv_req_i,
output rsp_t [NrPorts-1:0] slv_rsp_o,
output req_t mst_req_o,
input rsp_t mst_rsp_i
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_REQ_CHAN_T(req_chan_t, addr_t, data_t, strb_t)
localparam int unsigned LogNrPorts = cf_math_pkg::idx_width(NrPorts);
logic [NrPorts-1:0] req_valid_masked, req_ready_masked;
logic [LogNrPorts-1:0] idx, idx_rsp;
logic full;
req_chan_t [NrPorts-1:0] req_payload_q;
logic [NrPorts-1:0] req_valid_q, req_ready_q;
// Unforunately we need this signal otherwise the simulator complains about
// multiple driven signals, because some other signals are driven from an
// `always_comb` block.
logic [NrPorts-1:0] slv_rsp_q_ready;
// Optionially cut the incoming path
for (genvar i = 0; i < NrPorts; i++) begin : gen_cuts
spill_register #(
.T (req_chan_t),
.Bypass (!RegisterReq[i])
) i_spill_register_req (
.clk_i,
.rst_ni,
.valid_i (slv_req_i[i].q_valid),
.ready_o (slv_rsp_q_ready[i]),
.data_i (slv_req_i[i].q),
.valid_o (req_valid_q[i]),
.ready_i (req_ready_masked[i]),
.data_o (req_payload_q[i])
);
end
// We need to silence the handshake in case the fifo is full and we can't
// accept more transactions.
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
rr_arb_tree #(
.NumIn (NrPorts),
.DataType (req_chan_t),
.AxiVldRdy (1'b1),
.LockIn (1'b1)
) i_q_mux (
.clk_i,
.rst_ni,
.flush_i (1'b0),
.rr_i ('0),
.req_i (req_valid_masked),
.gnt_o (req_ready_q),
.data_i (req_payload_q),
.gnt_i (mst_rsp_i.q_ready),
.req_o (mst_req_o.q_valid),
.data_o (mst_req_o.q),
.idx_o ()
);
// De-generate version does not need a fifo. We always know where to route
// back the responses.
if (NrPorts == 1) begin : gen_single_port
assign idx_rsp = 0;
assign full = 1'b0;
end else begin : gen_multi_port
// For the "normal" case we need to safe the arbitration decision. We so by
// converting the handshake into a binary signal which we save for response
// routing.
onehot_to_bin #(
.ONEHOT_WIDTH (NrPorts)
) i_onehot_to_bin (
.onehot (req_valid_q & req_ready_q),
.bin (idx)
);
// Safe the arbitration decision.
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),
.push_i (mst_req_o.q_valid & mst_rsp_i.q_ready),
.data_o (idx_rsp),
.pop_i (mst_req_o.p_ready & mst_rsp_i.p_valid)
);
end
// Output Mux
always_comb begin
for (int i = 0; i < NrPorts; i++) begin
slv_rsp_o[i].p_valid = '0;
slv_rsp_o[i].q_ready = slv_rsp_q_ready[i];
slv_rsp_o[i].p = mst_rsp_i.p;
end
slv_rsp_o[idx_rsp].p_valid = mst_rsp_i.p_valid;
end
assign mst_req_o.p_ready = slv_req_i[idx_rsp].p_ready;
endmodule
`include "reqrsp_interface/typedef.svh"
`include "reqrsp_interface/assign.svh"
/// Interface wrapper.
module reqrsp_mux_intf #(
/// Number of input ports.
parameter int unsigned NrPorts = 2,
/// Address width of the interface.
parameter int unsigned AddrWidth = 0,
/// Data width of the interface.
parameter int unsigned DataWidth = 0,
/// Amount of outstanding responses. Determines the FIFO size.
parameter int unsigned RespDepth = 8,
/// Cut timing paths on the request path. Incurs a cycle additional latency.
/// Registers are inserted at the slave side.
parameter bit [NrPorts-1:0] RegisterReq = '0
) (
input logic clk_i,
input logic rst_ni,
REQRSP_BUS slv [NrPorts],
REQRSP_BUS mst
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] strb_t;
`REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t)
reqrsp_req_t [NrPorts-1:0] reqrsp_slv_req;
reqrsp_rsp_t [NrPorts-1:0] reqrsp_slv_rsp;
reqrsp_req_t reqrsp_mst_req;
reqrsp_rsp_t reqrsp_mst_rsp;
reqrsp_mux #(
.NrPorts (NrPorts),
.AddrWidth (AddrWidth),
.DataWidth (DataWidth),
.req_t (reqrsp_req_t),
.rsp_t (reqrsp_rsp_t),
.RespDepth (RespDepth),
.RegisterReq (RegisterReq)
) i_reqrsp_mux (
.clk_i,
.rst_ni,
.slv_req_i (reqrsp_slv_req),
.slv_rsp_o (reqrsp_slv_rsp),
.mst_req_o (reqrsp_mst_req),
.mst_rsp_i (reqrsp_mst_rsp)
);
for (genvar i = 0; i < NrPorts; i++) begin : gen_interface_assignment
`REQRSP_ASSIGN_TO_REQ(reqrsp_slv_req[i], slv[i])
`REQRSP_ASSIGN_FROM_RESP(slv[i], reqrsp_slv_rsp[i])
end
`REQRSP_ASSIGN_FROM_REQ(mst, reqrsp_mst_req)
`REQRSP_ASSIGN_TO_RESP(reqrsp_mst_rsp, mst)
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
/// Package containing common req resp definitions.
package reqrsp_pkg;
/// Size field. Same semantic as AXI.
typedef logic [2:0] size_t;
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_e;
/// The given operation falls into the atomic fetch-and-op memory operations.
function automatic logic is_amo(amo_op_e amo);
if (amo inside {AMOSwap, AMOAdd, AMOAnd, AMOOr, AMOXor, AMOMax, AMOMaxu, AMOMin, AMOMinu}) begin
return 1;
end else begin
return 0;
end
endfunction
/// Translate to AXI5+ATOP AMOs
function automatic logic [5:0] to_axi_amo(amo_op_e amo);
logic [5:0] result;
unique case (amo)
AMOSwap: result = axi_pkg::ATOP_ATOMICSWAP;
AMOAdd: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
// Careful, this case needs to invert the bits on the write data bus.
AMOAnd: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
AMOOr: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
AMOXor: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
AMOMax: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
AMOMaxu: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
AMOMin: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
AMOMinu: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
default: result = '0;
endcase
return result;
endfunction
/// Translate from AXI5+ATOP AMOs
function automatic amo_op_e from_axi_amo(axi_pkg::atop_t amo);
amo_op_e result;
unique case (amo)
axi_pkg::ATOP_ATOMICSWAP: result = AMOSwap;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD}: result = AMOAdd;
// Careful, this case needs to invert the bits on the write data bus.
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR}: result = AMOAnd;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET}: result = AMOOr;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR}: result = AMOXor;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX}: result = AMOMax;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX}: result = AMOMaxu;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN}: result = AMOMin;
{axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN}: result = AMOMinu;
default: result = AMONone;
endcase
return result;
endfunction
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>
`include "reqrsp_interface/typedef.svh"
`include "reqrsp_interface/assign.svh"
`include "axi/typedef.svh"
`include "axi/assign.svh"
module axi_to_reqrsp_tb import reqrsp_pkg::*; #(
parameter int unsigned AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned IW = 2,
parameter int unsigned UW = 2,
parameter int unsigned NrReads = 1000,
parameter int unsigned NrWrites = 1000
);
localparam time ClkPeriod = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
logic clk, rst_n;
// interfaces
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave_dv (clk);
AXI_BUS #(
.AXI_ADDR_WIDTH ( AW ),
.AXI_DATA_WIDTH ( DW ),
.AXI_ID_WIDTH ( IW ),
.AXI_USER_WIDTH ( UW )
) master ();
AXI_BUS_DV #(
.AXI_ADDR_WIDTH ( AW ),
.AXI_DATA_WIDTH ( DW ),
.AXI_ID_WIDTH ( IW ),
.AXI_USER_WIDTH ( UW )
) master_dv (clk);
axi_to_reqrsp_intf #(
.AddrWidth (AW),
.DataWidth (DW),
.IdWidth (IW),
.UserWidth (UW),
.BufDepth (4)
) dut (
.clk_i (clk),
.rst_ni (rst_n),
.busy_o (),
.axi (master),
.reqrsp (slave)
);
`AXI_ASSIGN(master, master_dv)
`REQRSP_ASSIGN(slave_dv, slave)
// ----------------
// Clock generation
// ----------------
initial begin
rst_n = 0;
repeat (3) begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
rst_n = 1;
forever begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
end
// -------
// Monitor
// -------
typedef reqrsp_test::reqrsp_monitor #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_monitor_t;
reqrsp_monitor_t reqrsp_monitor = new (slave_dv);
// Reqrsp Monitor.
initial begin
@(posedge rst_n)
reqrsp_monitor.monitor();
end
// AXI Monitor
typedef axi_test::axi_monitor #(
// AXI interface parameters
.AW ( AW ),
.DW ( DW ),
.IW ( IW ),
.UW ( UW ),
// Stimuli application and test time
.TT ( TestTime )
) axi_monitor_t;
axi_monitor_t axi_monitor = new (master_dv);
initial begin
@(posedge rst_n);
axi_monitor.monitor();
end
// ----------
// Scoreboard
// ----------
initial begin
automatic int unsigned id_cnt_read;
automatic int unsigned id_cnt_write;
forever begin
automatic reqrsp_test::req_t req;
automatic reqrsp_test::rsp_t rsp;
automatic axi_monitor_t::ax_beat_t ax;
automatic axi_monitor_t::b_beat_t b;
automatic axi_monitor_t::r_beat_t r;
automatic axi_monitor_t::w_beat_t w;
reqrsp_monitor.req_mbx.get(req);
reqrsp_monitor.rsp_mbx.get(rsp);
// Check that we have seen the appropriate transactions on the inputs.
if (req.write) begin
axi_monitor.aw_mbx.peek(ax);
axi_monitor.w_mbx.get(w);
// Invert bits as this is signalled as a clear condition on AXI.
if (req.amo == AMOAnd) begin
req.data = ~req.data;
end
assert(req.size == ax.ax_size)
else $error("[Write Size] Expected `%h` got `%h`", ax.ax_size, req.size);
assert(req.strb == w.w_strb)
else $error("[Write Strb] Expected `%h` got `%h`", w.w_strb, req.strb);
assert(req.data == w.w_data)
else $error("[Write Data] Expected `%h` got `%h`", w.w_data, req.data);
assert(req.write == 1);
assert (
req.addr ==
axi_pkg::beat_addr(ax.ax_addr, ax.ax_size, ax.ax_len, ax.ax_burst, id_cnt_write)
) else $error("[Write Addr] Expected `%h` got `%h`.", ax.ax_addr, req.addr);
id_cnt_write++;
// Check SCs
assert(ax.ax_lock == (req.amo == AMOSC))
else $error("[Write Amo] Expected `%h` got `%h`", ax.ax_lock, (req.amo == AMOSC));
// Consume Rs for atomic instructions.
if (ax.ax_atop[5:4] == axi_pkg::ATOP_ATOMICLOAD ||
ax.ax_atop inside {axi_pkg::ATOP_ATOMICSWAP, axi_pkg::ATOP_ATOMICCMP}) begin
axi_monitor.r_mbx.get(r);
assert(req.amo == from_axi_amo(ax.ax_atop));
end
if (w.w_last) begin
axi_monitor.aw_mbx.get(ax);
axi_monitor.b_mbx.get(b);
id_cnt_write = 0;
assert(ax.ax_id == b.b_id);
end
end else begin
axi_monitor.ar_mbx.peek(ax);
axi_monitor.r_mbx.get(r);
assert(req.size == ax.ax_size)
else $error("[Read Size] Expected `%h` got `%h`", ax.ax_size, req.size);
assert(rsp.data == r.r_data)
else $error("[Read Data] Expected `%h` got `%h`", r.r_data, rsp.data);
assert(ax.ax_id == r.r_id);
assert(req.write == 0);
assert(ax.ax_lock == (req.amo == AMOLR))
else $error("[Read Amo] Expected `%h` got `%h`", ax.ax_lock, (req.amo == AMOLR));
assert(
req.addr ==
axi_pkg::beat_addr(ax.ax_addr, ax.ax_size, ax.ax_len, ax.ax_burst, id_cnt_read)
) else $error("[Read Addr] Expected `%h` got `%h`.", ax.ax_addr, req.addr);
id_cnt_read++;
if (r.r_last) begin
id_cnt_read = 0;
axi_monitor.ar_mbx.get(ax);
end
end
end
end
// Check that all transcations ceased.
final begin
assert(axi_monitor.aw_mbx.num() == 0);
assert(axi_monitor.w_mbx.num() == 0);
assert(axi_monitor.b_mbx.num() == 0);
assert(axi_monitor.ar_mbx.num() == 0);
assert(axi_monitor.r_mbx.num() == 0);
end
// ------
// Driver
// ------
// AXI Master
typedef axi_test::axi_rand_master #(
// AXI interface parameters
.AW ( AW ),
.DW ( DW ),
.IW ( IW ),
.UW ( UW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime ),
.MAX_READ_TXNS (10),
.MAX_WRITE_TXNS (10),
.AX_MIN_WAIT_CYCLES (0),
.AX_MAX_WAIT_CYCLES (20),
// Only incremental bursts are supported.
.AXI_BURST_FIXED (0),
// Limit the burst length, to reduce simulation time.
.AXI_MAX_BURST_LEN (10),
// Check for exclusive accesses and atops.
.AXI_EXCLS (1),
.AXI_ATOPS (1)
) rand_axi_master_t;
rand_axi_master_t axi_rand_master = new (master_dv);
// AXI Random master.
initial begin
axi_rand_master.reset();
@(posedge rst_n);
axi_rand_master.run(NrReads, NrWrites);
// Wait until all transactions have ceased.
repeat(1000) @(posedge clk);
$finish;
end
typedef reqrsp_test::rand_reqrsp_slave #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_slave_t;
reqrsp_rand_slave_t rand_reqrsp_slave = new (slave_dv);
// Reqrsp Slave.
initial begin
rand_reqrsp_slave.reset();
@(posedge rst_n);
rand_reqrsp_slave.run();
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>
`include "reqrsp_interface/assign.svh"
/// Testbench for `reqrsp_demux`. Random drivers on the slave and master ports
/// drive a random pattern. The monitors track all packets and the scoreboard
/// checks the integrity of the schedule and data. We test the demux on a fixed,
/// address-based routing scheme. That makes it deterministic which port should
/// have received the corresponding datum and we can use the scoreboard to track
/// this.
module reqrsp_demux_tb import reqrsp_pkg::*; #(
parameter int unsigned AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned NrPorts = 4,
parameter int unsigned RespDepth = 2,
parameter int unsigned NrRandomTransactions = 1000
);
localparam time ClkPeriod = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
localparam int unsigned SelectWidth = cf_math_pkg::idx_width(NrPorts);
typedef logic [SelectWidth-1:0] select_t;
logic clk, rst_n;
select_t slv_select;
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master_dv (clk);
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave [NrPorts]();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave_dv [NrPorts] (clk);
reqrsp_demux_intf #(
.NrPorts (NrPorts),
.AddrWidth (AW),
.DataWidth (DW),
.RespDepth (RespDepth)
) dut (
.clk_i (clk),
.rst_ni (rst_n),
.slv_select_i (slv_select),
.slv (master),
.mst (slave)
);
assign slv_select = master.q_addr % NrPorts;
`REQRSP_ASSIGN(master, master_dv)
for (genvar i = 0; i < NrPorts; i++) begin : gen_if_assignment
`REQRSP_ASSIGN(slave_dv[i], slave[i])
end
// ----------------
// Clock generation
// ----------------
initial begin
rst_n = 0;
repeat (3) begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
rst_n = 1;
forever begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
end
// -------
// Monitor
// -------
typedef reqrsp_test::reqrsp_monitor #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_monitor_t;
reqrsp_monitor_t reqrsp_mst_monitor = new (master_dv);
// Reqrsp Monitor.
initial begin
@(posedge rst_n);
reqrsp_mst_monitor.monitor();
end
reqrsp_monitor_t reqrsp_slv_monitor [NrPorts];
for (genvar i = 0; i < NrPorts; i++) begin : gen_mst_mon
initial begin
reqrsp_slv_monitor[i] = new (slave_dv[i]);
@(posedge rst_n);
reqrsp_slv_monitor[i].monitor();
end
end
// ------
// Driver
// ------
typedef reqrsp_test::rand_reqrsp_slave #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_slave_t;
reqrsp_rand_slave_t rand_reqrsp_slave [NrPorts];
for (genvar i = 0; i < NrPorts; i++) begin : gen_slv_driver
initial begin
rand_reqrsp_slave[i] = new (slave_dv[i]);
rand_reqrsp_slave[i].reset();
@(posedge rst_n);
rand_reqrsp_slave[i].run();
end
end
typedef reqrsp_test::rand_reqrsp_master #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_master_t;
reqrsp_rand_master_t rand_reqrsp_master = new (master_dv);
// Reqrsp master.
initial begin
rand_reqrsp_master.reset();
@(posedge rst_n);
rand_reqrsp_master.run(NrRandomTransactions);
// Wait until all transactions have ceased.
repeat(1000) @(posedge clk);
$finish;
end
// ----------
// Scoreboard
// ----------
initial begin
forever begin
automatic reqrsp_test::req_t req;
automatic reqrsp_test::req_t req_slv;
automatic reqrsp_test::rsp_t rsp;
automatic reqrsp_test::rsp_t rsp_slv;
automatic select_t select;
reqrsp_mst_monitor.req_mbx.get(req);
reqrsp_mst_monitor.rsp_mbx.get(rsp);
// Check that for each master transaction we see a slave transaction on
// the right port.
// The slave select in the testbench is steered based on the
// (randomized) address.
select = req.addr % NrPorts;
reqrsp_slv_monitor[select].req_mbx.get(req_slv);
reqrsp_slv_monitor[select].rsp_mbx.get(rsp_slv);
assert(req_slv.do_compare(req));
assert(rsp_slv.do_compare(rsp));
end
end
// Check that we have associated all transactions.
final begin
assert(reqrsp_mst_monitor.req_mbx.num() == 0);
assert(reqrsp_mst_monitor.rsp_mbx.num() == 0);
for (int i = 0; i < NrPorts; i++) begin
assert(reqrsp_slv_monitor[i].req_mbx.num() == 0);
assert(reqrsp_slv_monitor[i].rsp_mbx.num() == 0);
end
$display("Checked for non-empty mailboxes.");
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>
`include "reqrsp_interface/assign.svh"
/// Testbench for all idempotent modules i.e., module which do not alter data
/// but only the handshaking.
module reqrsp_idempotent_tb import reqrsp_pkg::*; #(
parameter int unsigned AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned NrRandomTransactions = 1000,
/// Test Isochronous spill register.
parameter bit Iso = 1,
/// Test normal spill register.
parameter bit Cut = 0
);
localparam time ClkPeriod = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
logic clk, rst_n;
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master_dv (clk);
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave_dv (clk);
// Isochronous crossing.
if (Iso) begin : gen_iso_dut
// We only want to test that the module is properly wired up so we can put it
// in bypass which makes this testbench significantly easier as we do not need
// to generate a isochronous clock. The iso feature is tested in a separate
// testbench in the common_cells repository.
reqrsp_iso_intf #(
.AddrWidth (AW),
.DataWidth (DW),
.BypassReq (1),
.BypassRsp (1)
) i_dut (
.src_clk_i (clk),
.src_rst_ni (rst_n),
.src (master),
.dst_clk_i (clk),
.dst_rst_ni (rst_n),
.dst (slave)
);
// Plain synchronous cuts.
end else if (Cut) begin : gen_cut_dut
reqrsp_cut_intf #(
.AddrWidth (AW),
.DataWidth (DW),
.BypassReq (0),
.BypassRsp (0)
) i_dut (
.clk_i (clk),
.rst_ni (rst_n),
.slv (master),
.mst (slave)
);
end
`REQRSP_ASSIGN(master, master_dv)
`REQRSP_ASSIGN(slave_dv, slave)
// ----------------
// Clock generation
// ----------------
initial begin
rst_n = 0;
repeat (3) begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
rst_n = 1;
forever begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
end
// -------
// Monitor
// -------
typedef reqrsp_test::reqrsp_monitor #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_monitor_t;
reqrsp_monitor_t reqrsp_mst_monitor = new (master_dv);
// Reqrsp Monitor.
initial begin
@(posedge rst_n);
reqrsp_mst_monitor.monitor();
end
reqrsp_monitor_t reqrsp_slv_monitor = new (slave_dv);
// Reqrsp Monitor.
initial begin
@(posedge rst_n);
reqrsp_slv_monitor.monitor();
end
// ------
// Driver
// ------
typedef reqrsp_test::rand_reqrsp_slave #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_slave_t;
reqrsp_rand_slave_t rand_reqrsp_slave = new (slave_dv);
initial begin
rand_reqrsp_slave.reset();
@(posedge rst_n);
rand_reqrsp_slave.run();
end
typedef reqrsp_test::rand_reqrsp_master #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_master_t;
reqrsp_rand_master_t rand_reqrsp_master = new (master_dv);
// Reqrsp master.
initial begin
rand_reqrsp_master.reset();
@(posedge rst_n);
rand_reqrsp_master.run(NrRandomTransactions);
// Wait until all transactions have ceased.
repeat(1000) @(posedge clk);
$finish;
end
// ----------
// Scoreboard
// ----------
initial begin
forever begin
automatic reqrsp_test::req_t req;
automatic reqrsp_test::req_t req_slv;
automatic reqrsp_test::rsp_t rsp;
automatic reqrsp_test::rsp_t rsp_slv;
reqrsp_mst_monitor.req_mbx.get(req);
reqrsp_mst_monitor.rsp_mbx.get(rsp);
reqrsp_slv_monitor.req_mbx.get(req_slv);
reqrsp_slv_monitor.rsp_mbx.get(rsp_slv);
assert(req_slv.do_compare(req));
assert(rsp_slv.do_compare(rsp));
end
end
// Check that we have associated all transactions.
final begin
assert(reqrsp_mst_monitor.req_mbx.num() == 0);
assert(reqrsp_mst_monitor.rsp_mbx.num() == 0);
assert(reqrsp_slv_monitor.req_mbx.num() == 0);
assert(reqrsp_slv_monitor.rsp_mbx.num() == 0);
$display("Checked for non-empty mailboxes.");
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>
`include "reqrsp_interface/assign.svh"
/// Testbench for `reqrsp_mux`. Random drivers on the slave and master ports
/// drive a random pattern. The monitors track all packets and the scoreboard
/// tries to generate a legit schedule. If the testbench terminates and no
/// schedule could be found the request and response routing of the arbiter is
/// wrong.
module reqrsp_mux_tb import reqrsp_pkg::*; #(
parameter int unsigned AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned NrPorts = 4,
parameter int unsigned RespDepth = 2,
parameter int unsigned RegisterReq = 1,
parameter int unsigned NrRandomTransactions = 100
);
localparam time ClkPeriod = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
logic clk, rst_n;
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master [NrPorts] ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master_dv [NrPorts] (clk);
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) slave_dv (clk);
reqrsp_mux_intf #(
.NrPorts (NrPorts),
.AddrWidth (AW),
.DataWidth (DW),
.RespDepth (RespDepth),
.RegisterReq (RegisterReq)
) dut (
.clk_i (clk),
.rst_ni (rst_n),
.slv (master),
.mst (slave)
);
`REQRSP_ASSIGN(slave_dv, slave)
for (genvar i = 0; i < NrPorts; i++) begin : gen_if_assignment
`REQRSP_ASSIGN(master[i], master_dv[i])
end
// ----------------
// Clock generation
// ----------------
initial begin
rst_n = 0;
repeat (3) begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
rst_n = 1;
forever begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
end
// -------
// Monitor
// -------
typedef reqrsp_test::reqrsp_monitor #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_monitor_t;
reqrsp_monitor_t reqrsp_slv_monitor = new (slave_dv);
// Reqrsp Monitor.
initial begin
@(posedge rst_n);
reqrsp_slv_monitor.monitor();
end
reqrsp_monitor_t reqrsp_mst_monitor [NrPorts];
for (genvar i = 0; i < NrPorts; i++) begin : gen_mst_mon
initial begin
reqrsp_mst_monitor[i] = new (master_dv[i]);
@(posedge rst_n);
reqrsp_mst_monitor[i].monitor();
end
end
// ------
// Driver
// ------
typedef reqrsp_test::rand_reqrsp_master #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_master_t;
reqrsp_rand_master_t rand_reqrsp_master [NrPorts];
for (genvar i = 0; i < NrPorts; i++) begin : gen_mst_driver
initial begin
rand_reqrsp_master[i] = new (master_dv[i]);
rand_reqrsp_master[i].reset();
@(posedge rst_n);
rand_reqrsp_master[i].run(NrRandomTransactions);
end
end
typedef reqrsp_test::rand_reqrsp_slave #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_rand_slave_t;
reqrsp_rand_slave_t rand_reqrsp_slave = new (slave_dv);
// Reqrsp Slave.
initial begin
rand_reqrsp_slave.reset();
@(posedge rst_n);
rand_reqrsp_slave.run();
end
// ----------
// Scoreboard
// ----------
// The arbiter is a bit more tricky to test as we do not, looking from
// outside, know where the arbitration decision actually went (because two
// exactly same requests can be issued and on the output port we have lost all
// information on which port this request originated). This scoreboard
// therefore takes a greedy approach. It tries to map each packet on the
// output to a corresponding packet observed on a input port. The first port
// that matches this will be assigned as the source and the monitor packets
// from this port will be removed.
// While the request does not necessarily must have come from that exact port,
// the exact order doesn't matter as long as all packets have been removed and
// all mailboxes are empty.
initial begin
automatic int unsigned nr_transactions = 0;
forever begin
automatic reqrsp_test::req_t req;
automatic reqrsp_test::rsp_t rsp;
automatic bit arb_found = 0;
reqrsp_slv_monitor.req_mbx.get(req);
reqrsp_slv_monitor.rsp_mbx.get(rsp);
nr_transactions++;
// Check that this transaction has been valid at one of the request
// ports.
for (int i = 0; i < NrPorts; i++) begin
// Check that the request mailbox contains at least one value, otherwise
// one early finishing port can stall the rest. Also, if the request is
// observeable on the output the input must have handshaked, so this is
// a safe operation.
if (reqrsp_mst_monitor[i].req_mbx.num() != 0) begin
automatic reqrsp_test::req_t req_inp;
automatic reqrsp_test::rsp_t rsp_inp;
reqrsp_mst_monitor[i].req_mbx.peek(req_inp);
reqrsp_mst_monitor[i].rsp_mbx.peek(rsp_inp);
if (req_inp.do_compare(req) && rsp_inp.do_compare(rsp)) begin
reqrsp_mst_monitor[i].req_mbx.get(req_inp);
reqrsp_mst_monitor[i].rsp_mbx.get(rsp_inp);
arb_found |= 1;
break;
end
end
end
assert(arb_found) else $error("No arbitration found.");
if (nr_transactions == NrPorts * NrRandomTransactions) $finish;
end
end
// Check that we have associated all transactions.
final begin
assert(reqrsp_slv_monitor.req_mbx.num() == 0);
assert(reqrsp_slv_monitor.rsp_mbx.num() == 0);
for (int i = 0; i < NrPorts; i++) begin
assert(reqrsp_mst_monitor[i].req_mbx.num() == 0);
assert(reqrsp_mst_monitor[i].rsp_mbx.num() == 0);
end
$display("Checked for non-empty mailboxes.");
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>
`include "reqrsp_interface/assign.svh"
`include "axi/assign.svh"
/// Testbench for the request/response TB
module reqrsp_to_axi_tb import reqrsp_pkg::*; #(
parameter int unsigned AW = 32,
parameter int unsigned DW = 32,
parameter int unsigned IW = 2,
parameter int unsigned UW = 2,
parameter int unsigned NrDirectedReads = 2000,
parameter int unsigned NrDirectedWrites = 2000,
parameter int unsigned NrRandomTransactions = 4000
);
localparam time ClkPeriod = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
logic clk, rst_n;
typedef logic [AW-1:0] addr_t;
typedef logic [DW-1:0] data_t;
typedef logic [DW/8-1:0] strb_t;
typedef logic [IW-1:0] id_t;
typedef logic [UW-1:0] user_t;
// interfaces
REQRSP_BUS #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master ();
REQRSP_BUS_DV #(
.ADDR_WIDTH ( AW ),
.DATA_WIDTH ( DW )
) master_dv (clk);
AXI_BUS #(
.AXI_ADDR_WIDTH ( AW ),
.AXI_DATA_WIDTH ( DW ),
.AXI_ID_WIDTH ( IW ),
.AXI_USER_WIDTH ( UW )
) slave ();
AXI_BUS_DV #(
.AXI_ADDR_WIDTH ( AW ),
.AXI_DATA_WIDTH ( DW ),
.AXI_ID_WIDTH ( IW ),
.AXI_USER_WIDTH ( UW )
) slave_dv (clk);
reqrsp_to_axi_intf #(
.AxiIdWidth (IW),
.AddrWidth (AW),
.DataWidth (DW),
.AxiUserWidth (UW)
) i_reqrsp_to_axi (
.clk_i (clk),
.rst_ni (rst_n),
.reqrsp (master),
.axi (slave)
);
`REQRSP_ASSIGN(master, master_dv)
`AXI_ASSIGN(slave_dv, slave)
// ----------------
// Clock generation
// ----------------
initial begin
rst_n = 0;
repeat (3) begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
rst_n = 1;
forever begin
#(ClkPeriod/2) clk = 0;
#(ClkPeriod/2) clk = 1;
end
end
// -------
// Monitor
// -------
typedef reqrsp_test::reqrsp_monitor #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_monitor_t;
reqrsp_monitor_t reqrsp_monitor = new (master_dv);
// Reqrsp Monitor.
initial begin
@(posedge rst_n);
reqrsp_monitor.monitor();
end
// AXI Monitor
typedef axi_test::axi_monitor #(
// AXI interface parameters
.AW ( AW ),
.DW ( DW ),
.IW ( IW ),
.UW ( UW ),
// Stimuli application and test time
.TT ( TestTime )
) axi_monitor_t;
axi_monitor_t axi_monitor = new (slave_dv);
initial begin
@(posedge rst_n);
axi_monitor.monitor();
end
// ----------
// Scoreboard
// ----------
initial begin
forever begin
automatic reqrsp_test::req_t req;
automatic reqrsp_test::rsp_t rsp;
automatic axi_monitor_t::ax_beat_t ax;
automatic axi_monitor_t::b_beat_t b;
automatic axi_monitor_t::r_beat_t r;
automatic axi_monitor_t::w_beat_t w;
reqrsp_monitor.req_mbx.get(req);
// check fields match
// Writes and atomics.
// For each write the reqrsp bus we want to see a `aw` beat.
if (req.write) begin
axi_monitor.aw_mbx.get(ax);
axi_monitor.w_mbx.get(w);
if (req.amo != AMOAnd) begin
assert(w.w_data == req.data)
else $error("[Data Check] Expecting `%b` got `%b`", w.w_data, req.data);
end else begin
assert(w.w_data == (~req.data))
else $error("[AMOAnd] Expecting `%b` got `%b`", w.w_data, req.data);
end
// Check byte strobe.
assert(w.w_strb == req.strb);
// Check exclusive access.
assert(req.amo != AMOSC || ax.ax_lock == 1);
axi_monitor.b_mbx.get(b);
reqrsp_monitor.rsp_mbx.get(rsp);
// Check error flag.
assert(rsp.error == b.b_resp[1])
else $error("[Write Resp] Expecting `%h` got `%h`.", b.b_resp[1], rsp.error);
// Check whether we are expecting an additional R response.
if (is_amo(req.amo)) begin
// Check that AMOs were correctly translated.
assert(ax.ax_atop == to_axi_amo(req.amo))
else $error("Expecting `%b` got `%b`", to_axi_amo(req.amo), ax.ax_atop);
axi_monitor.r_mbx.get(r);
assert(rsp.data == r.r_data)
else $error("[Write Amo] Expecting `%h` got `%h`.", r.r_data, rsp.data);
end
// Reads
end else begin
axi_monitor.ar_mbx.get(ax);
axi_monitor.r_mbx.get(r);
// Check exclusive access.
assert(req.amo != AMOLR || ax.ax_lock == 1);
reqrsp_monitor.rsp_mbx.get(rsp);
// Check read data access.
assert(rsp.data == r.r_data)
else $error("Expecting `%h` got `%h` on read data.", r.r_data, rsp.data);
// Check error flag.
assert(rsp.error == r.r_resp[1])
else $error("[Read Response] Expecting `%h` got `%h`.", r.r_resp[1], rsp.error);
end
assert(ax.ax_len == 0)
else $fatal("Testbench does not support bursts.");
assert(ax.ax_size == req.size);
assert(req.addr == ax.ax_addr)
else $error("Address does noit match. Expected `%h` got `%h`.", req.addr, ax.ax_addr);
end
end
// Check that all transcations ceased.
final begin
assert(axi_monitor.aw_mbx.num() == 0);
assert(axi_monitor.w_mbx.num() == 0);
assert(axi_monitor.b_mbx.num() == 0);
assert(axi_monitor.ar_mbx.num() == 0);
assert(axi_monitor.r_mbx.num() == 0);
end
// ------
// Driver
// ------
// AXI Driver
typedef axi_test::axi_rand_slave #(
// AXI interface parameters
.AW ( AW ),
.DW ( DW ),
.IW ( IW ),
.UW ( UW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime ),
.RAND_RESP (1),
.AX_MIN_WAIT_CYCLES (0),
.AX_MAX_WAIT_CYCLES (20)
) rand_axi_slave_t;
rand_axi_slave_t axi_rand_slave = new (slave_dv);
typedef reqrsp_test::rand_reqrsp_master #(
// Reqrsp bus interface paramaters;
.AW ( AW ),
.DW ( DW ),
// Stimuli application and test time
.TA ( ApplTime ),
.TT ( TestTime )
) reqrsp_driver_t;
reqrsp_driver_t rand_reqrsp_master = new (master_dv);
// AXI side.
initial begin
axi_rand_slave.reset();
@(posedge rst_n);
axi_rand_slave.run();
end
// Reqrsp master side.
initial begin
rand_reqrsp_master.reset();
@(posedge rst_n);
// Directed testing.
// 1. Directed testing (read).
fork
repeat(NrDirectedReads) begin
automatic reqrsp_test::req_t req = new;
assert(req.randomize() with { amo == AMONone; write == 0;});
rand_reqrsp_master.drv.send_req(req);
end
repeat (NrDirectedReads) begin
automatic reqrsp_test::rsp_t rsp;
rand_reqrsp_master.drv.recv_rsp(rsp);
end
join
// 2. Directed testing (write).
fork
repeat(NrDirectedWrites) begin
automatic reqrsp_test::req_t req = new;
assert(req.randomize() with { amo == AMONone; write == 1;});
rand_reqrsp_master.drv.send_req(req);
end
repeat (NrDirectedWrites) begin
automatic reqrsp_test::rsp_t rsp;
rand_reqrsp_master.drv.recv_rsp(rsp);
end
join
// 3. Random testing.
rand_reqrsp_master.run(NrRandomTransactions);
$info("Rand reqrsp master finished. Waiting for completion.");
// Wait until req/rsp mailboxes are empty.
while (reqrsp_monitor.req_mbx.num() > 0 || reqrsp_monitor.rsp_mbx.num() > 0) begin
@(posedge clk);
end
$info("Finished Testing %d vectors", NrRandomTransactions + NrDirectedReads + NrDirectedWrites);
$finish;
end
endmodule
#!/bin/bash
# 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>
# Andreas Kurth <akurth@iis.ee.ethz.ch>
set -e
[ ! -z "$VSIM" ] || VSIM=vsim
bender script vsim -t test \
--vlog-arg="-svinputport=compat" \
--vlog-arg="-override_timescale 1ns/1ps" \
--vlog-arg="-suppress 2583" \
--vlog-arg="+cover=sbecft" \
> compile.tcl
echo 'return 0' >> compile.tcl
$VSIM -c -do 'exit -code [source compile.tcl]'
#!/bin/bash
# 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>
# Andreas Kurth <akurth@iis.ee.ethz.ch>
set -e
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
[ ! -z "$VSIM" ] || VSIM=vsim
call_vsim() {
echo "log -r /*; run -all" | $VSIM -c -coverage -voptargs='+acc +cover=sbecft' "$@" | tee vsim.log 2>&1
grep "Errors: 0," vsim.log
}
call_vsim axi_to_reqrsp_tb
call_vsim reqrsp_to_axi_tb
call_vsim reqrsp_mux_tb
call_vsim reqrsp_demux_tb
# Test `reqrsp_cut`
call_vsim reqrsp_idempotent_tb -gCut=1 -gIso=0
# Test `reqrsp_iso`
call_vsim reqrsp_idempotent_tb -gCut=0 -gIso=1
# Copyright 2020 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
*.o
*.so
*.log
*.vcd
.Trash*
*.fsdb
Bender.lock
.bender/
modelsim.ini
trace_hart_*
transcript
vsim.wlf
work/
wave*
*vstf
wlf*
.vlogansetup.args
.vlogansetup.env
AN.DB/
csrc/
simv
simv.daidir/
ucli.key
vc_hdrs.h
vlogan.txt
# Copyright 2020 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
package:
name: snitch
authors: [ "Florian Zaruba <zarubaf@iis.ee.ethz.ch>" ]
dependencies:
common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.19.0 }
sources:
- defines:
SNITCH_ENABLE_PERF: 1
files:
# packages
- src/riscv_instr.sv
- src/snitch_pkg.sv
- src/snitch_axi_pkg.sv
- src/snitch_icache/snitch_icache_pkg.sv
# rest of RTL
- src/snitch.sv
- src/snitch_regfile_ff.sv
# - src/snitch_regfile_latch.sv
- src/snitch_lsu.sv
- src/snitch_ipu.sv
- src/snitch_shared_muldiv.sv
- src/snitch_demux.sv
- src/snitch_axi_adapter.sv
- src/snitch_icache/snitch_icache.sv
- src/snitch_icache/snitch_icache_l0.sv
- src/snitch_icache/snitch_icache_handler.sv
- src/snitch_icache/snitch_icache_lfsr.sv
- src/snitch_icache/snitch_icache_lookup_parallel.sv
- src/snitch_icache/snitch_icache_lookup_serial.sv
- src/snitch_icache/snitch_icache_refill.sv
- src/snitch_read_only_cache/snitch_axi_to_cache.sv
- src/snitch_read_only_cache/snitch_read_only_cache.sv
// Copyright 2018 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>
// Date: 17.8.2018
//
// Description: Contains Snitch's AXI ports, does not contain user ports
package snitch_axi_pkg;
localparam UserWidth = 1;
localparam AddrWidth = 32;
localparam DataWidth = 32;
localparam StrbWidth = DataWidth / 8;
typedef logic [snitch_pkg::IdWidth-1:0] id_t;
typedef logic [snitch_pkg::IdWidthSlave-1:0] id_slv_t;
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [StrbWidth-1:0] strb_t;
typedef logic [UserWidth-1:0] user_t;
// AW Channel
typedef struct packed {
id_t id;
addr_t addr;
axi_pkg::len_t len;
axi_pkg::size_t size;
axi_pkg::burst_t burst;
logic lock;
axi_pkg::cache_t cache;
axi_pkg::prot_t prot;
axi_pkg::qos_t qos;
axi_pkg::region_t region;
axi_pkg::atop_t atop;
} aw_chan_t;
// AW Channel - Slave
typedef struct packed {
id_slv_t id;
addr_t addr;
axi_pkg::len_t len;
axi_pkg::size_t size;
axi_pkg::burst_t burst;
logic lock;
axi_pkg::cache_t cache;
axi_pkg::prot_t prot;
axi_pkg::qos_t qos;
axi_pkg::region_t region;
axi_pkg::atop_t atop;
} aw_chan_slv_t;
// W Channel - AXI4 doesn't define a wid
typedef struct packed {
data_t data;
strb_t strb;
logic last;
} w_chan_t;
// B Channel
typedef struct packed {
id_t id;
axi_pkg::resp_t resp;
} b_chan_t;
// B Channel - Slave
typedef struct packed {
id_slv_t id;
axi_pkg::resp_t resp;
} b_chan_slv_t;
// AR Channel
typedef struct packed {
id_t id;
addr_t addr;
axi_pkg::len_t len;
axi_pkg::size_t size;
axi_pkg::burst_t burst;
logic lock;
axi_pkg::cache_t cache;
axi_pkg::prot_t prot;
axi_pkg::qos_t qos;
axi_pkg::region_t region;
} ar_chan_t;
// AR Channel - Slave
typedef struct packed {
id_slv_t id;
addr_t addr;
axi_pkg::len_t len;
axi_pkg::size_t size;
axi_pkg::burst_t burst;
logic lock;
axi_pkg::cache_t cache;
axi_pkg::prot_t prot;
axi_pkg::qos_t qos;
axi_pkg::region_t region;
} ar_chan_slv_t;
// R Channel
typedef struct packed {
id_t id;
data_t data;
axi_pkg::resp_t resp;
logic last;
} r_chan_t;
// R Channel - Slave
typedef struct packed {
id_slv_t id;
data_t data;
axi_pkg::resp_t resp;
logic last;
} r_chan_slv_t;
// Request/Response structs
typedef struct packed {
aw_chan_t aw;
logic aw_valid;
w_chan_t w;
logic w_valid;
logic b_ready;
ar_chan_t ar;
logic ar_valid;
logic r_ready;
} req_t;
typedef struct packed {
logic aw_ready;
logic ar_ready;
logic w_ready;
logic b_valid;
b_chan_t b;
logic r_valid;
r_chan_t r;
} resp_t;
typedef struct packed {
aw_chan_slv_t aw;
logic aw_valid;
w_chan_t w;
logic w_valid;
logic b_ready;
ar_chan_slv_t ar;
logic ar_valid;
logic r_ready;
} req_slv_t;
typedef struct packed {
logic aw_ready;
logic ar_ready;
logic w_ready;
logic b_valid;
b_chan_slv_t b;
logic r_valid;
r_chan_slv_t r;
} resp_slv_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
/// 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 req_valid_mask;
logic req_ready_mask;
logic [LogNrPorts-1:0] idx, idx_r, idx_w, idx_rsp;
logic full_r, full_w, 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_q[i] ),
.data_o ( req_payload_q[i] )
);
end
assign req_valid_o = req_valid_mask & ~full;
assign req_ready_mask = req_ready_i & ~full;
/// 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_q ),
.inp_ready_o ( req_ready_q ),
.oup_data_o ( req_payload_o ),
.oup_valid_o ( req_valid_mask ),
.oup_ready_i ( req_ready_mask )
);
if (NrPorts == 1) begin : gen_connection
assign idx_rsp = 0;
assign full = 1'b0;
end else begin : gen_demux
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_r_resp_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( full_r ),
.empty_o ( ),
.usage_o ( ),
.data_i ( idx ),
.push_i ( req_valid_o && req_ready_i && !req_payload_o.write ),
.data_o ( idx_r ),
.pop_i ( resp_ready_o && resp_valid_i && resp_last_i && !resp_payload_i.write )
);
fifo_v3 #(
.DATA_WIDTH ( LogNrPorts ),
.DEPTH ( RespDepth )
) i_w_resp_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( full_w ),
.empty_o ( ),
.usage_o ( ),
.data_i ( idx ),
.push_i ( req_valid_o && req_ready_i && req_payload_o.write ),
.data_o ( idx_w ),
.pop_i ( resp_ready_o && resp_valid_i && resp_last_i && resp_payload_i.write )
);
assign idx_rsp = resp_payload_i.write ? idx_w : idx_r;
assign full = req_payload_o.write ? full_w : full_r;
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
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