From 800d836091072c50b25cacb7fa51d6a1b2e94a61 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 12 Jun 2024 11:11:10 +0200 Subject: [PATCH 01/14] Add openocd test job Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/scripts/openocd_test.sh | 81 +++++ .github/scripts/utils.sh | 63 ++++ .github/workflows/build-openocd.yml | 54 ++++ .github/workflows/ci.yml | 11 +- .github/workflows/test-openocd.yml | 82 ++++++ testbench/asm/infinite_loop.ld | 13 + testbench/asm/infinite_loop.s | 47 +++ testbench/jtagdpi/README.md | 25 ++ testbench/jtagdpi/jtagdpi.c | 210 +++++++++++++ testbench/jtagdpi/jtagdpi.h | 56 ++++ testbench/jtagdpi/jtagdpi.sv | 47 +++ testbench/tb_top.sv | 29 +- testbench/tcp_server/tcp_server.c | 439 ++++++++++++++++++++++++++++ testbench/tcp_server/tcp_server.h | 71 +++++ tools/Makefile | 21 +- tools/openocd/common.tcl | 35 +++ tools/openocd/jtag_cg.tcl | 77 +++++ tools/openocd/sim-jtagdpi.cfg | 11 + tools/openocd/veer-el2-rst.cfg | 29 ++ tools/openocd/verilator-rst.cfg | 5 + 20 files changed, 1397 insertions(+), 9 deletions(-) create mode 100755 .github/scripts/openocd_test.sh create mode 100644 .github/scripts/utils.sh create mode 100644 .github/workflows/build-openocd.yml create mode 100644 .github/workflows/test-openocd.yml create mode 100644 testbench/asm/infinite_loop.ld create mode 100644 testbench/asm/infinite_loop.s create mode 100644 testbench/jtagdpi/README.md create mode 100644 testbench/jtagdpi/jtagdpi.c create mode 100644 testbench/jtagdpi/jtagdpi.h create mode 100644 testbench/jtagdpi/jtagdpi.sv create mode 100644 testbench/tcp_server/tcp_server.c create mode 100644 testbench/tcp_server/tcp_server.h create mode 100644 tools/openocd/common.tcl create mode 100644 tools/openocd/jtag_cg.tcl create mode 100644 tools/openocd/sim-jtagdpi.cfg create mode 100644 tools/openocd/veer-el2-rst.cfg create mode 100644 tools/openocd/verilator-rst.cfg diff --git a/.github/scripts/openocd_test.sh b/.github/scripts/openocd_test.sh new file mode 100755 index 00000000000..dcfc87f4d1f --- /dev/null +++ b/.github/scripts/openocd_test.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This script runs Verilator RTL simulation in background and invokes OpenOCD +# to perform JTAG access test + +SIM_LOG=`realpath sim.log` +OPENOCD_LOG=`realpath openocd.log` + +set +e + +if [ "$#" -lt 1 ]; then + echo "Usage: openocd_test.sh [openocd args ...]" + exit 1 +fi +OPENOCD_ARGS=$@ + +# Utils +source `dirname ${BASH_SOURCE[0]}`/utils.sh + +print_logs () { + echo -e "${COLOR_WHITE}======== Simulation log ========${COLOR_OFF}" + cat ${SIM_LOG} || true + echo -e "${COLOR_WHITE}======== OpenOCD log ========${COLOR_OFF}" + cat ${OPENOCD_LOG} || true +} + +echo -e "${COLOR_WHITE}======== Launching interactive simulation ========${COLOR_OFF}" + +# Start the simulation +echo -e "Starting simulation..." +obj_dir/Vtb_top >"${SIM_LOG}" 2>&1 & +SIM_PID=$! + +# Wait +wait_for_phrase "${SIM_LOG}" "VerilatorTB: Start of sim" +if [ $? -ne 0 ]; then + echo -e "${COLOR_RED}Failed to start the simulation!${COLOR_OFF}" + print_logs + terminate ${SIM_PID}; exit -1 +fi +echo -e "Simulation running and ready (pid=${SIM_PID})" + +# Wait a bit +sleep 2s + +# Run the test +echo -e "${COLOR_WHITE}======== Running OpenOCD test '$@' ========${COLOR_OFF}" +cd ${RV_ROOT}/tools/openocd && openocd -d2 ${OPENOCD_ARGS} >"${OPENOCD_LOG}" 2>&1 +EXITCODE=$? + +if [ ${EXITCODE} -eq 0 ]; then + echo -e "${COLOR_GREEN}[PASSED]${COLOR_OFF}" +else + echo -e "${COLOR_RED}[FAILED]${COLOR_OFF}" +fi + +sleep 1s + +# Terminate +echo -e "${COLOR_WHITE}Terminating...${COLOR_OFF}" +terminate ${SIM_PID} + +# Display logs +print_logs + +# Honor the exitcode +exit ${EXITCODE} + diff --git a/.github/scripts/utils.sh b/.github/scripts/utils.sh new file mode 100644 index 00000000000..24bd8324275 --- /dev/null +++ b/.github/scripts/utils.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Colors +COLOR_OFF='\033[0m' +COLOR_RED='\033[31m' +COLOR_GREEN='\033[32m' +COLOR_WHITE='\033[1;37m' + +# Waits until the given phrase appears in a log file (actively written to) +# Usage: wait_for_phrase +wait_for_phrase () { + + # Check if the log exists + sleep 1s + if ! [ -f "$1" ]; then + echo -e "${COLOR_RED}Log file '$1' not found!${COLOR_OFF}" + return -1 + fi + + # Wait for the phrase + DEADLINE=$((${EPOCHSECONDS} + 30)) + while [ ${EPOCHSECONDS} -lt ${DEADLINE} ] + do + # Check for the phrase + grep "$2" "$1" >/dev/null + if [ $? -eq 0 ]; then + return 0 + fi + + # Sleep and retry + sleep 1s + done + + # Timeout + return -1 +} + +# Terminates a process. First via SIGINT and if this doesn't work after 10s +# retries with SIGKILL +# Usage: terminate +terminate () { + + local PID=$1 + + # Gently interrupt, wait some time and then kill + /bin/kill -s SIGINT ${PID} || true + sleep 10s + /bin/kill -s SIGKILL ${PID} || true +} diff --git a/.github/workflows/build-openocd.yml b/.github/workflows/build-openocd.yml new file mode 100644 index 00000000000..ee7e43a6f9e --- /dev/null +++ b/.github/workflows/build-openocd.yml @@ -0,0 +1,54 @@ +name: OpenOCD Build + +on: + workflow_call: + +jobs: + openOCD: + name: Build OpenOCD + runs-on: ubuntu-latest + env: + # A custom fork is needed to allow bypassing core examination and accessing + # peripherals regardless of core state. + OPENOCD_REPO: https://github.com/antmicro/openocd + OPENOCD_VERSION: riscv-nohalt + + steps: + - name: Setup Cache Metadata + id: cache_metadata + run: | + cache_date=$(date +"%Y_%m_%d") + cache_name=cache_openocd + echo "Cache date: "$cache_date + echo "Cache name: "$cache_name + echo "cache_date=$cache_date" >> "$GITHUB_ENV" + echo "cache_name=$cache_name" >> "$GITHUB_ENV" + + - name: Setup cache + uses: actions/cache@v3 + id: cache + timeout-minutes: 60 + with: + path: | + /opt/openocd + /opt/openocd/.cache + key: ${{ env.cache_name }}_${{ env.cache_date }} + restore-keys: ${{ env.cache_name }}_ + + - name: Install prerequisities + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + run: | + sudo apt -qqy update && sudo apt -qqy --no-install-recommends install \ + make libtool pkg-config autoconf automake texinfo + + - name: Build and install OpenOCD + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + run: | + git clone -b "${OPENOCD_VERSION}" "${OPENOCD_REPO}" + pushd openocd + ./bootstrap + ./configure --prefix=/opt/openocd --enable-remote-bitbang \ + CFLAGS="-Wno-error=misleading-indentation -Wno-error=stringop-overflow" + make -j`nproc` + sudo make install + popd diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3bf089daa4f..e1e7c3ceb56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,10 @@ jobs: name: Build-Spike uses: ./.github/workflows/build-spike.yml + Build-OpenOCD: + name: Build-OpenOCD + uses: ./.github/workflows/build-openocd.yml + Test-Regression: name: Test-Regression needs: [Build-Verilator] @@ -50,9 +54,14 @@ jobs: name: Test-Renode uses: ./.github/workflows/test-renode.yml + Test-OpenOCD: + name: Test-OpenOCD + needs: [Build-Verilator, Build-OpenOCD] + uses: ./.github/workflows/test-openocd.yml + Report-Coverage: name: Report-Coverage - needs: [Test-Regression, Test-Verification, Test-Microarchitectural, Test-RISCV-DV, Test-RISCOF] + needs: [Test-Regression, Test-Verification, Test-Microarchitectural, Test-RISCV-DV, Test-RISCOF, Test-OpenOCD] uses: ./.github/workflows/report-coverage.yml Build-Docs: diff --git a/.github/workflows/test-openocd.yml b/.github/workflows/test-openocd.yml new file mode 100644 index 00000000000..3583fe86900 --- /dev/null +++ b/.github/workflows/test-openocd.yml @@ -0,0 +1,82 @@ +name: Test-OpenOCD + +on: + workflow_call: + +jobs: + + tests: + name: Run OpenOCD tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + coverage: ["all", "branch", "toggle"] #TODO: add functional coverage + env: + DEBIAN_FRONTEND: "noninteractive" + CCACHE_DIR: "/opt/openocd-tests/.cache/" + VERILATOR_VERSION: v5.010 + + steps: + - name: Install utils + run: | + sudo apt -qqy update && sudo apt -qqy --no-install-recommends install \ + cpanminus ccache ninja-build gcc-riscv64-unknown-elf + pip3 install meson + sudo cpanm Bit::Vector + + - name: Setup Cache Metadata + id: cache_metadata + run: | + date=$(date +"%Y_%m_%d") + time=$(date +"%Y%m%d_%H%M%S_%N") + cache_verilator_restore_key=cache_verilator_ + cache_verilator_key=${cache_verilator_restore_key}${{ env.VERILATOR_VERSION }} + cache_openocd_restore_key=cache_openocd_ + cache_openocd_key=${cache_openocd_restore_key} + cache_test_restore_key=${{ matrix.coverage }}_ + cache_test_key=${cache_test_restore_key}${time} + echo "date=$date" | tee -a "$GITHUB_ENV" + echo "time=$time" | tee -a "$GITHUB_ENV" + echo "cache_verilator_restore_key=$cache_verilator_restore_key" | tee -a "$GITHUB_ENV" + echo "cache_verilator_key=$cache_verilator_key" | tee -a "$GITHUB_ENV" + echo "cache_openocd_restore_key=$cache_openocd_restore_key" | tee -a "$GITHUB_ENV" + echo "cache_openocd_key=$cache_openocd_key" | tee -a "$GITHUB_ENV" + echo "cache_test_restore_key=$cache_test_restore_key" | tee -a "$GITHUB_ENV" + echo "cache_test_key=$cache_test_key" | tee -a "$GITHUB_ENV" + + - name: Restore Verilator cache + id: cache-verilator-restore + uses: actions/cache/restore@v3 + with: + path: | + /opt/verilator + /opt/verilator/.cache + key: ${{ env.cache_verilator_key }} + restore-keys: ${{ env.cache_verilator_restore_key }} + + - name: Restore OpenOCD cache + id: cache-openocd-restore + uses: actions/cache/restore@v3 + with: + path: | + /opt/openocd + /opt/openocd/.cache + key: ${{ env.cache_openocd_key }} + restore-keys: ${{ env.cache_openocd_restore_key }} + + - name: Setup repository + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Build verilated simulation + run: | + export PATH=/opt/verilator/bin:/opt/openocd/bin:$PATH + export RV_ROOT=$(pwd) + mkdir run + make -C run -f ${RV_ROOT}/tools/Makefile verilator-build program.hex TEST=infinite_loop + cd run + ${RV_ROOT}/.github/scripts/openocd_test.sh \ + -f ${RV_ROOT}/tools/openocd/verilator-rst.cfg \ + -f ${RV_ROOT}/tools/openocd/jtag_cg.tcl diff --git a/testbench/asm/infinite_loop.ld b/testbench/asm/infinite_loop.ld new file mode 100644 index 00000000000..0692d8c1410 --- /dev/null +++ b/testbench/asm/infinite_loop.ld @@ -0,0 +1,13 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS { + .text : { *(.text*) } + . = 0x10000; + .data : { *(.*data) *(.rodata*)} + . = ALIGN(4); + printf_start = .; + . = 0xee000000; + .data_load : AT(printf_start) {*(.data_text)} + printf_end = printf_start + SIZEOF(.data_load); +} diff --git a/testbench/asm/infinite_loop.s b/testbench/asm/infinite_loop.s new file mode 100644 index 00000000000..41037ed264c --- /dev/null +++ b/testbench/asm/infinite_loop.s @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2019 Western Digital Corporation or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Copied hello_world_iccm with an infinite loop inserted at the end + +#include "defines.h" + +#define STDOUT 0xd0580000 + + .set mfdc, 0x7f9 +.extern printf_start, printf_end +// Code to execute +.section .text +.global _start +_start: + + + + // Enable Caches in MRAC + li x1, 0x5f555555 + csrw 0x7c0, x1 + li x3, 4 + csrw mfdc, x3 // disable store merging + + // Simple infinite loop program with inner and outer loop + li t3, 0 +outer: + addi t3, t3, 1 + li t4, 123 +inner: + addi t4, t4, -1 + bne t4, zero, inner + jal x0, outer +.long 0,1,2,3,4 diff --git a/testbench/jtagdpi/README.md b/testbench/jtagdpi/README.md new file mode 100644 index 00000000000..54750ef1f6b --- /dev/null +++ b/testbench/jtagdpi/README.md @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# # Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 +# # Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +JTAG DPI module for OpenOCD remote_bitbang driver +================================================= + +This DPI module provides a "virtual" JTAG connection between a simulated chip +and [OpenOCD](http://openocd.org/). It makes use of the `remote_bitbang` JTAG +driver shipped with OpenOCD, which forwards JTAG requests over TCP to a remote +server. The `jtagdpi` module is instantiated in the hardware simulation to +receive the JTAG requests from OpenOCD and drive the JTAG pins (TCK, TMS, TDI, +etc.) from it. + +The `remote_bitbang` protocol is documented in the OpenOCD source tree at +`doc/manual/jtag/drivers/remote_bitbang.txt`, or online at +https://repo.or.cz/openocd.git/blob/HEAD:/doc/manual/jtag/drivers/remote_bitbang.txt diff --git a/testbench/jtagdpi/jtagdpi.c b/testbench/jtagdpi/jtagdpi.c new file mode 100644 index 00000000000..e0b2708748e --- /dev/null +++ b/testbench/jtagdpi/jtagdpi.c @@ -0,0 +1,210 @@ +// Copyright lowRISC contributors. +// Copyright 2023 Antmicro. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "jtagdpi.h" + +#include +#include +#include +#include +#include + +#include "tcp_server.h" + +// Uncomment to enable JTAG DPI debugging. The code will print vertically +// oriented waveform for all JTAG signals. +//#define JTAGDPI_DEBUG + +struct jtagdpi_signals { + uint8_t tck; + uint8_t tms; + uint8_t tdi; + uint8_t tdo; + uint8_t trst_n; + uint8_t srst_n; +}; + +struct jtagdpi_ctx { + // Server context + struct tcp_server_ctx *sock; + // Signals + struct jtagdpi_signals curr; +#ifdef JTAGDPI_DEBUG + struct jtagdpi_signals prev; + uint8_t init; +#endif +}; + +/** + * Reset the JTAG signals to a "dongle unplugged" state + */ +static void reset_jtag_signals(struct jtagdpi_ctx *ctx) { + assert(ctx); + + // Set all to zero + memset(&ctx->curr, 0, sizeof(struct jtagdpi_signals)); +#ifdef JTAGDPI_DEBUG + memset(&ctx->prev, 0, sizeof(struct jtagdpi_signals)); +#endif + + // trst_n is pulled down (reset active) by default + // srst_n is pulled up (reset not active) by default + ctx->curr.srst_n = 1; +#ifdef JTAGDPI_DEBUG + ctx->prev.srst_n = 1; +#endif +} + +/** + * Update the JTAG signals in the context structure + */ +static void update_jtag_signals(struct jtagdpi_ctx *ctx) { + assert(ctx); + + /* + * Documentation pointer: + * The remote_bitbang protocol implemented below is documented in the OpenOCD + * source tree at doc/manual/jtag/drivers/remote_bitbang.txt, or online at + * https://repo.or.cz/openocd.git/blob/HEAD:/doc/manual/jtag/drivers/remote_bitbang.txt + */ + + // read a command byte + char cmd; + if (!tcp_server_read(ctx->sock, &cmd)) { + return; + } + + bool act_send_resp = false; + bool act_quit = false; + + // parse received command byte + if (cmd >= '0' && cmd <= '7') { + // JTAG write + char cmd_bit = cmd - '0'; + ctx->curr.tdi = (cmd_bit >> 0) & 0x1; + ctx->curr.tms = (cmd_bit >> 1) & 0x1; + ctx->curr.tck = (cmd_bit >> 2) & 0x1; + } else if (cmd >= 'r' && cmd <= 'u') { + // JTAG reset (active high from OpenOCD) + char cmd_bit = cmd - 'r'; + ctx->curr.srst_n = !((cmd_bit >> 0) & 0x1); + ctx->curr.trst_n = !((cmd_bit >> 1) & 0x1); + } else if (cmd == 'R') { + // JTAG read + act_send_resp = true; + } else if (cmd == 'B') { + // printf("%s: BLINK ON!\n", ctx->display_name); + } else if (cmd == 'b') { + // printf("%s: BLINK OFF!\n", ctx->display_name); + } else if (cmd == 'Q') { + // quit (client disconnect) + act_quit = true; + } else { + fprintf(stderr, + "JTAG DPI Protocol violation detected: unsupported command %c\n", + cmd); + exit(1); + } + + // send tdo as response + if (act_send_resp) { + char tdo_ascii = ctx->curr.tdo + '0'; + tcp_server_write(ctx->sock, tdo_ascii); + } + + if (act_quit) { + printf("JTAG DPI: Remote disconnected.\n"); + tcp_server_client_close(ctx->sock); + } +} + +void *jtagdpi_create(const char *display_name, int listen_port) { + struct jtagdpi_ctx *ctx = + (struct jtagdpi_ctx *)calloc(1, sizeof(struct jtagdpi_ctx)); + assert(ctx); + + // Create socket + ctx->sock = tcp_server_create(display_name, listen_port); +#ifdef JTAGDPI_DEBUG + ctx->init = 1; +#endif + + reset_jtag_signals(ctx); + + printf( + "\n" + "JTAG: Virtual JTAG interface %s is listening on port %d. Use\n" + "OpenOCD and the following configuration to connect:\n" + " interface remote_bitbang\n" + " remote_bitbang_host localhost\n" + " remote_bitbang_port %d\n", + display_name, listen_port, listen_port); + + return (void *)ctx; +} + +void jtagdpi_close(void *ctx_void) { + struct jtagdpi_ctx *ctx = (struct jtagdpi_ctx *)ctx_void; + if (!ctx) { + return; + } + tcp_server_close(ctx->sock); + free(ctx); +} + +#ifdef JTAGDPI_DEBUG +static void jtagdpi_dbg(struct jtagdpi_ctx *ctx) { + + uint8_t* curr = (uint8_t*)&ctx->curr; + uint8_t* prev = (uint8_t*)&ctx->prev; + + if (ctx->init) { + fprintf(stderr, "tck tms tdi tdo trst srst\n"); + ctx->init = 0; + } + + for (int i=0; i<6; ++i) { + if (!prev[i] && curr[i]) { + fprintf(stderr, "\\ "); + } + if ( prev[i] && curr[i]) { + fprintf(stderr, " | "); + } + if ( prev[i] && !curr[i]) { + fprintf(stderr, "/ "); + } + if (!prev[i] && !curr[i]) { + fprintf(stderr, "| "); + } + } + fprintf(stderr, "\n"); +} +#endif + +void jtagdpi_tick(void *ctx_void, svBit *tck, svBit *tms, svBit *tdi, + svBit *trst_n, svBit *srst_n, const svBit tdo) { + struct jtagdpi_ctx *ctx = (struct jtagdpi_ctx *)ctx_void; + + // Get TDO + ctx->curr.tdo = tdo; + + // TODO: Evaluate moving this functionality into a separate thread + if (ctx) { + update_jtag_signals(ctx); + } + +#ifdef JTAGDPI_DEBUG + if (memcmp(&ctx->curr, &ctx->prev, sizeof(struct jtagdpi_signals))) { + jtagdpi_dbg(ctx); + memcpy(&ctx->prev, &ctx->curr, sizeof(struct jtagdpi_signals)); + } +#endif + + *tdi = ctx->curr.tdi; + *tms = ctx->curr.tms; + *tck = ctx->curr.tck; + *srst_n = ctx->curr.srst_n; + *trst_n = ctx->curr.trst_n; +} diff --git a/testbench/jtagdpi/jtagdpi.h b/testbench/jtagdpi/jtagdpi.h new file mode 100644 index 00000000000..9ecb2a1044e --- /dev/null +++ b/testbench/jtagdpi/jtagdpi.h @@ -0,0 +1,56 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_HW_DV_DPI_JTAGDPI_JTAGDPI_H_ +#define OPENTITAN_HW_DV_DPI_JTAGDPI_JTAGDPI_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct jtagdpi_ctx; + +/** + * Constructor: Create and initialize jtagdpi context object + * + * Call from a initial block. + * + * @param display_name Name of the JTAG interface (for display purposes only) + * @param listen_port Port to listen on + * @return an initialized struct jtagdpi_ctx context object + */ +void *jtagdpi_create(const char *display_name, int listen_port); + +/** + * Destructor: Close all connections and free all resources + * + * Call from a finish block. + * + * @param ctx_void a struct jtagdpi_ctx context object + */ +void jtagdpi_close(void *ctx_void); + +/** + * Drive JTAG signals + * + * Call this function from the simulation at every clock tick to read/write + * from/to the JTAG signals. + * + * @param ctx_void a struct jtagdpi_ctx context object + * @param tck JTAG test clock signal + * @param tms JTAG test mode select signal + * @param tdi JTAG test data input signal + * @param trst_n JTAG test reset signal (active low) + * @param srst_n JTAG system reset signal (active low) + * @param tdo JTAG test data out + */ +void jtagdpi_tick(void *ctx_void, svBit *tck, svBit *tms, svBit *tdi, + svBit *trst_n, svBit *srst_n, const svBit tdo); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // OPENTITAN_HW_DV_DPI_JTAGDPI_JTAGDPI_H_ diff --git a/testbench/jtagdpi/jtagdpi.sv b/testbench/jtagdpi/jtagdpi.sv new file mode 100644 index 00000000000..e69d719b5d6 --- /dev/null +++ b/testbench/jtagdpi/jtagdpi.sv @@ -0,0 +1,47 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module jtagdpi #( + parameter string Name = "jtag0", // name of the JTAG interface (display only) + parameter int ListenPort = 44853 // TCP port to listen on +)( + input logic clk_i, + input logic rst_ni, + + output logic jtag_tck, + output logic jtag_tms, + output logic jtag_tdi, + input logic jtag_tdo, + output logic jtag_trst_n, + output logic jtag_srst_n +); + + import "DPI-C" + function chandle jtagdpi_create(input string name, input int listen_port); + + import "DPI-C" + function void jtagdpi_tick(input chandle ctx, output bit tck, output bit tms, + output bit tdi, output bit trst_n, + output bit srst_n, input bit tdo); + + import "DPI-C" + function void jtagdpi_close(input chandle ctx); + + chandle ctx; + + initial begin + ctx = jtagdpi_create(Name, ListenPort); + end + + final begin + jtagdpi_close(ctx); + ctx = null; + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + jtagdpi_tick(ctx, jtag_tck, jtag_tms, jtag_tdi, jtag_trst_n, jtag_srst_n, + jtag_tdo); + end + +endmodule diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index ef043dd022d..aef0fa73fe3 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -99,6 +99,11 @@ module tb_top logic jtag_tdo; + logic jtag_tck; + logic jtag_tms; + logic jtag_tdi; + logic jtag_trst_n; + logic o_cpu_halt_ack; logic o_cpu_halt_status; logic o_cpu_run_ack; @@ -820,11 +825,11 @@ veer_wrapper rvtop_wrapper ( .trace_rv_i_interrupt_ip(trace_rv_i_interrupt_ip), .trace_rv_i_tval_ip (trace_rv_i_tval_ip), - .jtag_tck ( 1'b0 ), - .jtag_tms ( 1'b0 ), - .jtag_tdi ( 1'b0 ), - .jtag_trst_n ( 1'b0 ), - .jtag_tdo ( jtag_tdo ), + .jtag_tck (jtag_tck), + .jtag_tms (jtag_tms), + .jtag_tdi (jtag_tdi), + .jtag_trst_n (jtag_trst_n), + .jtag_tdo (jtag_tdo), .jtag_tdoEn (), .mpc_debug_halt_ack ( mpc_debug_halt_ack), @@ -1837,6 +1842,20 @@ for (genvar i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Simple buffer for passing data between TCP sockets and DPI modules + */ +#define BUFSIZE_BYTE 1024 // FIXME: This must be larger than the remote_bitbang + // buffer in OpenOCD. Otherwise deadlock occurs. + +struct tcp_buf { + unsigned int rptr; + unsigned int wptr; + char buf[BUFSIZE_BYTE]; +}; + +/** + * TCP Server thread context structure + */ +struct tcp_server_ctx { + // Writeable by the host thread + char *display_name; + uint16_t listen_port; + volatile bool socket_run; + // Writeable by the server thread + struct tcp_buf *buf_in; + struct tcp_buf *buf_out; + int sfd; // socket fd + int cfd; // client fd + pthread_t sock_thread; +}; + +static bool tcp_buffer_is_full(struct tcp_buf *buf) { + if (buf->wptr >= buf->rptr) { + return (buf->wptr - buf->rptr) == (BUFSIZE_BYTE - 1); + } else { + return (buf->rptr - buf->wptr) == 1; + } +} + +static bool tcp_buffer_is_empty(struct tcp_buf *buf) { + return (buf->wptr == buf->rptr); +} + +static void tcp_buffer_put_byte(struct tcp_buf *buf, char dat) { + bool done = false; + while (!done) { + if (!tcp_buffer_is_full(buf)) { + buf->buf[buf->wptr++] = dat; + buf->wptr %= BUFSIZE_BYTE; + done = true; + } + } +} + +static bool tcp_buffer_get_byte(struct tcp_buf *buf, char *dat) { + if (tcp_buffer_is_empty(buf)) { + return false; + } + *dat = buf->buf[buf->rptr++]; + buf->rptr %= BUFSIZE_BYTE; + return true; +} + +static struct tcp_buf *tcp_buffer_new(void) { + struct tcp_buf *buf_new; + buf_new = (struct tcp_buf *)malloc(sizeof(struct tcp_buf)); + buf_new->rptr = 0; + buf_new->wptr = 0; + return buf_new; +} + +static void tcp_buffer_free(struct tcp_buf **buf) { + free(*buf); + *buf = NULL; +} + +/** + * Start a TCP server + * + * This function creates attempts to create a new TCP socket instance. The + * socket is a non-blocking stream socket, with buffering disabled. + * + * @param ctx context object + * @return 0 on success, -1 in case of an error + */ +static int start(struct tcp_server_ctx *ctx) { + int rv; + + assert(ctx->sfd == 0 && "Server already started."); + + // create socket + int sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd == -1) { + fprintf(stderr, "%s: Unable to create socket: %s (%d)\n", ctx->display_name, + strerror(errno), errno); + return -1; + } + + rv = fcntl(sfd, F_SETFL, O_NONBLOCK); + if (rv != 0) { + fprintf(stderr, "%s: Unable to make socket non-blocking: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + return -1; + } + + // reuse existing socket (if existing) + int reuse_socket = 1; + rv = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)); + if (rv != 0) { + fprintf(stderr, "%s: Unable to set socket options: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + return -1; + } + + // stop tcp socket from buffering (buffering prevents timely responses to + // OpenOCD which severly limits debugging performance) + int tcp_nodelay = 1; + rv = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay, sizeof(int)); + if (rv != 0) { + fprintf(stderr, "%s: Unable to set socket nodelay: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + return -1; + } + + // bind server + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(ctx->listen_port); + + rv = bind(sfd, (struct sockaddr *)&addr, sizeof(addr)); + if (rv != 0) { + fprintf(stderr, "%s: Failed to bind socket: %s (%d)\n", ctx->display_name, + strerror(errno), errno); + return -1; + } + + // listen for incoming connections + rv = listen(sfd, 1); + if (rv != 0) { + fprintf(stderr, "%s: Failed to listen on socket: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + return -1; + } + + ctx->sfd = sfd; + assert(ctx->sfd > 0); + + return 0; +} + +/** + * Accept an incoming connection from a client (nonblocking) + * + * The resulting client fd is made non-blocking. + * + * @param ctx context object + * @return 0 on success, any other value indicates an error + */ +static int client_tryaccept(struct tcp_server_ctx *ctx) { + int rv; + + assert(ctx->sfd > 0); + assert(ctx->cfd == 0); + + int cfd = accept(ctx->sfd, NULL, NULL); + + if (cfd == -1 && errno == EAGAIN) { + return -EAGAIN; + } + + if (cfd == -1) { + fprintf(stderr, "%s: Unable to accept incoming connection: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + return -1; + } + + rv = fcntl(cfd, F_SETFL, O_NONBLOCK); + if (rv != 0) { + fprintf(stderr, "%s: Unable to make client socket non-blocking: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + return -1; + } + + ctx->cfd = cfd; + assert(ctx->cfd > 0); + + printf("%s: Accepted client connection\n", ctx->display_name); + + return 0; +} + +/** + * Stop the TCP server + * + * @param ctx context object + */ +static void stop(struct tcp_server_ctx *ctx) { + assert(ctx); + if (!ctx->sfd) { + return; + } + close(ctx->sfd); + ctx->sfd = 0; +} + +/** + * Receive a byte from a connected client + * + * @param ctx context object + * @param cmd byte received + * @return true if a byte was read + */ +static bool get_byte(struct tcp_server_ctx *ctx, char *cmd) { + assert(ctx); + + ssize_t num_read = read(ctx->cfd, cmd, 1); + + if (num_read == 0) { + return false; + } + if (num_read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } else if (errno == EBADF) { + // Possibly client went away? Accept a new connection. + fprintf(stderr, "%s: Client disappeared.\n", ctx->display_name); + tcp_server_client_close(ctx); + return false; + } else { + fprintf(stderr, "%s: Error while reading from client: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + assert(0 && "Error reading from client"); + } + } + assert(num_read == 1); + return true; +} + +/** + * Send a byte to a connected client + * + * @param ctx context object + * @param cmd byte to send + */ +static void put_byte(struct tcp_server_ctx *ctx, char cmd) { + while (1) { + ssize_t num_written = send(ctx->cfd, &cmd, sizeof(cmd), MSG_NOSIGNAL); + if (num_written == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else if (errno == EPIPE) { + printf("%s: Remote disconnected.\n", ctx->display_name); + tcp_server_client_close(ctx); + break; + } else { + fprintf(stderr, "%s: Error while writing to client: %s (%d)\n", + ctx->display_name, strerror(errno), errno); + assert(0 && "Error writing to client."); + } + } + if (num_written >= 1) { + break; + } + } +} + +/** + * Cleanup server context + * + * @param ctx context object + */ +static void ctx_free(struct tcp_server_ctx *ctx) { + // Free the buffers + tcp_buffer_free(&ctx->buf_in); + tcp_buffer_free(&ctx->buf_out); + // Free the display name + free(ctx->display_name); + // Free the ctx + free(ctx); + ctx = NULL; +} + +/** + * Thread function to create a new server instance + * + * @param ctx_void context object + * @return Always returns NULL + */ +static void *server_create(void *ctx_void) { + // Cast to a server struct + struct tcp_server_ctx *ctx = (struct tcp_server_ctx *)ctx_void; + struct timeval timeout; + + // Start the server + int rv = start(ctx); + if (rv != 0) { + fprintf(stderr, "%s: Unable to create TCP server on port %d\n", + ctx->display_name, ctx->listen_port); + goto err_cleanup_return; + } + + // Initialise timeout + timeout.tv_sec = 0; + + // Initialise fd_set + + // Start waiting for connection / data + char xfer_data; + while (ctx->socket_run) { + // Initialise structure of fds + fd_set read_fds; + FD_ZERO(&read_fds); + if (ctx->sfd) { + FD_SET(ctx->sfd, &read_fds); + } + if (ctx->cfd) { + FD_SET(ctx->cfd, &read_fds); + } + // max fd num + int mfd = (ctx->cfd > ctx->sfd) ? ctx->cfd : ctx->sfd; + + // Set timeout - 50us gives good performance + timeout.tv_usec = 50; + + // Wait for socket activity or timeout + rv = select(mfd + 1, &read_fds, NULL, NULL, &timeout); + + if (rv < 0) { + printf("%s: Socket read failed, port: %d\n", ctx->display_name, + ctx->listen_port); + tcp_server_client_close(ctx); + } + + // New connection + if (FD_ISSET(ctx->sfd, &read_fds)) { + client_tryaccept(ctx); + } + + // New client data + if (FD_ISSET(ctx->cfd, &read_fds)) { + while (get_byte(ctx, &xfer_data)) { + tcp_buffer_put_byte(ctx->buf_in, xfer_data); + } + } + + if (ctx->cfd != 0) { + while (tcp_buffer_get_byte(ctx->buf_out, &xfer_data)) { + put_byte(ctx, xfer_data); + } + } + } + +err_cleanup_return: + + // Simulation done - clean up + tcp_server_client_close(ctx); + stop(ctx); + + return NULL; +} + +// Abstract interface functions +struct tcp_server_ctx *tcp_server_create(const char *display_name, + int listen_port) { + struct tcp_server_ctx *ctx = + (struct tcp_server_ctx *)calloc(1, sizeof(struct tcp_server_ctx)); + assert(ctx); + + // Create the buffers + struct tcp_buf *buf_in = tcp_buffer_new(); + struct tcp_buf *buf_out = tcp_buffer_new(); + assert(buf_in); + assert(buf_out); + + // Populate the struct with buffer pointers + ctx->buf_in = buf_in; + ctx->buf_out = buf_out; + + // Set up socket details + ctx->socket_run = true; + ctx->listen_port = listen_port; + ctx->display_name = strdup(display_name); + assert(ctx->display_name); + + if (pthread_create(&ctx->sock_thread, NULL, server_create, (void *)ctx) != + 0) { + fprintf(stderr, "%s: Unable to create TCP socket thread\n", + ctx->display_name); + ctx_free(ctx); + free(ctx); + return NULL; + } + return ctx; +} + +bool tcp_server_read(struct tcp_server_ctx *ctx, char *dat) { + return tcp_buffer_get_byte(ctx->buf_in, dat); +} + +void tcp_server_write(struct tcp_server_ctx *ctx, char dat) { + tcp_buffer_put_byte(ctx->buf_out, dat); +} + +void tcp_server_close(struct tcp_server_ctx *ctx) { + // Shut down the socket thread + ctx->socket_run = false; + pthread_join(ctx->sock_thread, NULL); + ctx_free(ctx); +} + +void tcp_server_client_close(struct tcp_server_ctx *ctx) { + assert(ctx); + + if (!ctx->cfd) { + return; + } + + close(ctx->cfd); + ctx->cfd = 0; +} diff --git a/testbench/tcp_server/tcp_server.h b/testbench/tcp_server/tcp_server.h new file mode 100644 index 00000000000..5226322f90a --- /dev/null +++ b/testbench/tcp_server/tcp_server.h @@ -0,0 +1,71 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_HW_DV_DPI_COMMON_TCP_SERVER_TCP_SERVER_H_ +#define OPENTITAN_HW_DV_DPI_COMMON_TCP_SERVER_TCP_SERVER_H_ + +/** + * Functions to create and interact with a threaded TCP server + * + * This is intended to be used by simulation add-on DPI modules to provide + * basic TCP socket communication between a host and simulated peripherals. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +struct tcp_server_ctx; + +/** + * Non-blocking read of a byte from a connected client + * + * @param ctx tcp server context object + * @param dat byte received + * @return true if a byte was read + */ +bool tcp_server_read(struct tcp_server_ctx *ctx, char *dat); + +/** + * Write a byte to a connected client + * + * The write is internally buffered and so does not block if the client is not + * ready to accept data, but does block if the buffer is full. + * + * @param ctx tcp server context object + * @param dat byte to send + */ +void tcp_server_write(struct tcp_server_ctx *ctx, char dat); + +/** + * Create a new TCP server instance + * + * @param display_name C string description of server + * @param listen_port On which port the server should listen + * @return A pointer to the created context struct + */ +struct tcp_server_ctx *tcp_server_create(const char *display_name, + int listen_port); + +/** + * Shut down the server and free all reserved memory + * + * @param ctx tcp server context object + */ +void tcp_server_close(struct tcp_server_ctx *ctx); + +/** + * Instruct the server to disconnect a client + * + * @param ctx tcp server context object + */ +void tcp_server_client_close(struct tcp_server_ctx *ctx); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // OPENTITAN_HW_DV_DPI_COMMON_TCP_SERVER_TCP_SERVER_H_ diff --git a/tools/Makefile b/tools/Makefile index 443cb74d45d..cb92229f0a1 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -119,8 +119,23 @@ VPATH = $(TEST_DIR) $(BUILD_DIR) $(TBDIR) -include $(TEST_DIR)/$(TEST).mki +# Testbench DPI sources +TB_DPI_SRCS = jtagdpi/jtagdpi.c \ + tcp_server/tcp_server.c -TBFILES = $(TBDIR)/tb_top_pkg.sv $(TBDIR)/tb_top.sv $(TBDIR)/ahb_sif.sv +TB_DPI_INCS := $(addprefix -I$(TBDIR)/,$(dir $(TB_DPI_SRCS))) +# Add testbench include paths +CFLAGS += $(TB_DPI_INCS) + +TB_DPI_SRCS := $(addprefix $(TBDIR)/,$(TB_DPI_SRCS)) + +# Testbench sources +TB_VERILATOR_SRCS = $(TBDIR)/test_tb_top.cpp $(TB_DPI_SRCS) + +TBFILES = $(TBDIR)/tb_top_pkg.sv \ + $(TBDIR)/tb_top.sv \ + $(TBDIR)/ahb_sif.sv \ + $(TBDIR)/jtagdpi/jtagdpi.sv defines = $(BUILD_DIR)/common_defines.vh defines += ${RV_ROOT}/design/include/el2_def.sv @@ -151,12 +166,12 @@ clean: ${BUILD_DIR}/defines.h: BUILD_PATH=${BUILD_DIR} ${RV_ROOT}/configs/veer.config -target=$(target) $(CONF_PARAMS) -verilator-build: ${TBFILES} ${BUILD_DIR}/defines.h test_tb_top.cpp +verilator-build: ${TBFILES} ${BUILD_DIR}/defines.h $(TB_VERILATOR_SRCS) $(VERILATOR) --cc -CFLAGS "${CFLAGS}" $(defines) \ $(includes) -I${RV_ROOT}/testbench -f ${RV_ROOT}/testbench/flist \ -Wno-WIDTH -Wno-UNOPTFLAT $(VERILATOR_NOIMPLICIT) \ ${TBFILES} --top-module tb_top \ - -exe test_tb_top.cpp --autoflush $(VERILATOR_DEBUG) $(VERILATOR_COVERAGE) + -exe $(TB_VERILATOR_SRCS) --autoflush $(VERILATOR_DEBUG) $(VERILATOR_COVERAGE) cp ${RV_ROOT}/testbench/test_tb_top.cpp obj_dir/ $(MAKE) -e -C obj_dir/ -f Vtb_top.mk $(VERILATOR_MAKE_FLAGS) touch verilator-build diff --git a/tools/openocd/common.tcl b/tools/openocd/common.tcl new file mode 100644 index 00000000000..46017011fe6 --- /dev/null +++ b/tools/openocd/common.tcl @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +proc compare {x y} { + puts "'$x' vs. '$y'" + + if {[llength $y] != [llength $y]} { + puts "length mismatch!" + return -1 + } + + for {set i 0} {$i < [llength $x]} {incr i} { + if {[lindex $x $i] != [lindex $y $i]} { + puts "item $i mismatch!" + return -1 + } + } + + return 0 +} + +set STDOUT 0x300300cc +set dmstatus_addr 0x11 + diff --git a/tools/openocd/jtag_cg.tcl b/tools/openocd/jtag_cg.tcl new file mode 100644 index 00000000000..e15b0bb7c6d --- /dev/null +++ b/tools/openocd/jtag_cg.tcl @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +init + +set script_dir [file dirname [info script]] +source [file join $script_dir common.tcl] + +puts "Read Debug Module Status Register..." +set val [riscv dmi_read $dmstatus_addr] +puts "dmstatus: $val" +if {($val & 0x00000c00) == 0} { + echo "The hart is halted!" + shutdown error +} +puts "" + +riscv set_mem_access sysbus + +set addr1 0xFFFF0000 +set addr2 0x0000FFF0 +set addr3 0x0000FFFF +set data1 0x05050505 +set data2 0xFAFAFAFA +set data3 0xAB + +puts "Write few bytes" +write_memory $addr1 32 $data1 phys + +puts "Write few different bytes at the same address" +write_memory $addr1 32 $data2 phys + +puts "Read few bytes" +set actual [read_memory $addr1 32 1 phys] +if {[compare $actual $data2] != 0} { + shutdown error +} + +puts "Read few bytes one more time" +set actual [read_memory $addr1 32 1 phys] +if {[compare $actual $data2] != 0} { + shutdown error +} + +puts "Write few bytes to different address" +write_memory $addr2 32 $data1 phys + +puts "Read few bytes from that address" +set actual [read_memory $addr2 32 1 phys] +if {[compare $actual $data1] != 0} { + shutdown error +} + +puts "Write 1 byte" +write_memory $addr3 8 $data3 phys + +puts "Read 1 byte" +set actual [read_memory $addr3 8 1 phys] +if {[compare $actual $data3] != 0} { + shutdown error +} + +# Send signal to call $finish +write_memory 0xd0580000 8 0xFF phys + +shutdown diff --git a/tools/openocd/sim-jtagdpi.cfg b/tools/openocd/sim-jtagdpi.cfg new file mode 100644 index 00000000000..7d43a4ad5e3 --- /dev/null +++ b/tools/openocd/sim-jtagdpi.cfg @@ -0,0 +1,11 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# "JTAG adapter" for simulation, exposed to OpenOCD through a TCP socket +# speaking the remote_bitbang protocol. The adapter is implemented as +# SystemVerilog DPI module. + +adapter driver remote_bitbang +remote_bitbang port 5000 +remote_bitbang host localhost diff --git a/tools/openocd/veer-el2-rst.cfg b/tools/openocd/veer-el2-rst.cfg new file mode 100644 index 00000000000..c90f24266da --- /dev/null +++ b/tools/openocd/veer-el2-rst.cfg @@ -0,0 +1,29 @@ +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME riscv +} + +jtag newtap $_CHIPNAME tap -irlen 5 +set _TARGETNAME $_CHIPNAME.tap +target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME -rtos hwthread + +# Configure work area in on-chip SRAM +$_TARGETNAME.0 configure -work-area-phys 0x50001000 -work-area-size 0x1000 -work-area-backup 0 + +# Mem access mode +riscv set_mem_access sysbus + +# The following commands disable target examination and set explicitly the +# core parameters read from CSRs. These required a modified version of +# OpenOCD from https://github.com/antmicro/openocd/tree/riscv-nohalt +riscv set_nohalt on +riscv set_xlen 32 +riscv set_misa 0x40001104 + +# Be verbose about GDB errors +gdb_report_data_abort enable +gdb_report_register_access_error enable + +# Always use hardware breakpoints. +gdb_breakpoint_override hard diff --git a/tools/openocd/verilator-rst.cfg b/tools/openocd/verilator-rst.cfg new file mode 100644 index 00000000000..a8bfb01c331 --- /dev/null +++ b/tools/openocd/verilator-rst.cfg @@ -0,0 +1,5 @@ +source [find sim-jtagdpi.cfg] +source [find veer-el2-rst.cfg] + +# Increase timeouts in simulation +riscv set_command_timeout_sec 300 From 9593484996458f67057dba9d2484e1fddba91d57 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 20 Jun 2024 14:22:29 +0200 Subject: [PATCH 02/14] Add ahb_lite_2to1_mux Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- testbench/ahb_lite_2to1_mux.sv | 233 +++++++++++++++++++++++++++++++++ testbench/tb_top.sv | 84 ++++++++++-- tools/Makefile | 3 +- 3 files changed, 307 insertions(+), 13 deletions(-) create mode 100644 testbench/ahb_lite_2to1_mux.sv diff --git a/testbench/ahb_lite_2to1_mux.sv b/testbench/ahb_lite_2to1_mux.sv new file mode 100644 index 00000000000..5b9a2d56c60 --- /dev/null +++ b/testbench/ahb_lite_2to1_mux.sv @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// ------------------------------------------------------------- +// AHB Lite 2:1 Mux +// ------------------------------------------------------------- + +module ahb_lite_2to1_mux #( + parameter AHB_LITE_ADDR_WIDTH = 32, + parameter AHB_LITE_DATA_WIDTH = 32, + parameter AHB_NO_OPT = 0 +) ( + // --------------------------------------- + // Global clock/reset + // --------------------------------------- + input logic hclk, + input logic hreset_n, + input logic force_bus_idle, + + // --------------------------------------- + // From Initiator 0 + // --------------------------------------- + input logic hsel_i_0, + input logic [AHB_LITE_ADDR_WIDTH-1:0] haddr_i_0, + input logic [AHB_LITE_DATA_WIDTH-1:0] hwdata_i_0, + input logic hwrite_i_0, + input logic [1:0] htrans_i_0, + input logic [2:0] hsize_i_0, + input logic hready_i_0, + + output logic hresp_o_0, + output logic hready_o_0, + output logic [AHB_LITE_DATA_WIDTH-1:0] hrdata_o_0, + + // --------------------------------------- + // From Initiator 1 + // --------------------------------------- + input logic hsel_i_1, + input logic [AHB_LITE_ADDR_WIDTH-1:0] haddr_i_1, + input logic [AHB_LITE_DATA_WIDTH-1:0] hwdata_i_1, + input logic hwrite_i_1, + input logic [1:0] htrans_i_1, + input logic [2:0] hsize_i_1, + input logic hready_i_1, + + output logic hresp_o_1, + output logic hready_o_1, + output logic [AHB_LITE_DATA_WIDTH-1:0] hrdata_o_1, + + // --------------------------------------- + // To Responder Interface Port + // --------------------------------------- + input logic hresp_i, + input logic [AHB_LITE_DATA_WIDTH-1:0] hrdata_i, + input logic hreadyout_i, + + output logic [AHB_LITE_ADDR_WIDTH-1:0] haddr_o, + output logic [AHB_LITE_DATA_WIDTH-1:0] hwdata_o, + output logic hsel_o, + output logic hwrite_o, + output logic hready_o, + output logic [1:0] htrans_o, + output logic [2:0] hsize_o + +); + +//This is a fixed priority 2:1 mux for AHB-Lite protocol +//Initiator 0 always takes priority + +logic initiator0_address_ph, initiator1_address_ph; +logic initiator0_data_ph_nq, initiator1_data_ph_nq; +logic initiator0_data_ph, initiator1_data_ph; +logic initiator0_pend_addr_ph_nq, initiator1_pend_addr_ph_nq; +logic initiator0_pend_addr_ph, initiator1_pend_addr_ph; +logic initiator0_gnt, initiator1_gnt; +logic [AHB_LITE_ADDR_WIDTH-1:0] initiator0_pend_haddr, initiator1_pend_haddr; +logic [AHB_LITE_ADDR_WIDTH-1:0] initiator0_haddr, initiator1_haddr; +logic [1:0] initiator0_pend_htrans, initiator1_pend_htrans; +logic [1:0] initiator0_htrans, initiator1_htrans; +logic [2:0] initiator0_pend_hsize, initiator1_pend_hsize; +logic [2:0] initiator0_hsize, initiator1_hsize; +logic initiator0_pend_hwrite, initiator1_pend_hwrite; +logic initiator0_hwrite, initiator1_hwrite; + +//Detect address phase +always_comb initiator0_address_ph = hsel_i_0 & hready_i_0 & htrans_i_0 inside {2'b10, 2'b11} & ~force_bus_idle; +always_comb initiator1_address_ph = hsel_i_1 & hready_i_1 & htrans_i_1 inside {2'b10, 2'b11} & ~force_bus_idle; + +always_ff @(posedge hclk or negedge hreset_n) begin + if (~hreset_n) begin + initiator0_pend_haddr <= '0; + initiator1_pend_haddr <= '0; + initiator0_pend_htrans <= '0; + initiator1_pend_htrans <= '0; + initiator0_pend_hsize <= '0; + initiator1_pend_hsize <= '0; + initiator0_pend_hwrite <= '0; + initiator1_pend_hwrite <= '0; + initiator0_pend_addr_ph_nq <= '0; + initiator1_pend_addr_ph_nq <= '0; + initiator0_data_ph_nq <= '0; + initiator1_data_ph_nq <= '0; + end + else begin + //Capture the address during the address phase for each initiator + initiator0_pend_haddr <= initiator0_address_ph & ~initiator0_pend_addr_ph ? haddr_i_0 : initiator0_pend_haddr; + initiator1_pend_haddr <= initiator1_address_ph & ~initiator1_pend_addr_ph ? haddr_i_1 : initiator1_pend_haddr; + initiator0_pend_htrans <= initiator0_address_ph & ~initiator0_pend_addr_ph ? htrans_i_0 : initiator0_pend_htrans; + initiator1_pend_htrans <= initiator1_address_ph & ~initiator1_pend_addr_ph ? htrans_i_1 : initiator1_pend_htrans; + initiator0_pend_hsize <= initiator0_address_ph & ~initiator0_pend_addr_ph ? hsize_i_0 : initiator0_pend_hsize; + initiator1_pend_hsize <= initiator1_address_ph & ~initiator1_pend_addr_ph ? hsize_i_1 : initiator1_pend_hsize; + initiator0_pend_hwrite <= initiator0_address_ph & ~initiator0_pend_addr_ph ? hwrite_i_0 : initiator0_pend_hwrite; + initiator1_pend_hwrite <= initiator1_address_ph & ~initiator1_pend_addr_ph ? hwrite_i_1 : initiator1_pend_hwrite; + + //Capture pending address phase when initiators collide + initiator0_pend_addr_ph_nq <= (initiator0_address_ph | initiator0_pend_addr_ph) & ~(hreadyout_i & initiator0_gnt); + initiator1_pend_addr_ph_nq <= (initiator1_address_ph | initiator1_pend_addr_ph) & ~(hreadyout_i & initiator1_gnt); + + //Transition to data phase when endpoint accepts address phase, hold when not ready + initiator0_data_ph_nq <= (initiator0_gnt) | (initiator0_data_ph & ~hreadyout_i); + initiator1_data_ph_nq <= (initiator1_gnt) | (initiator1_data_ph & ~hreadyout_i); + end +end + +always_comb initiator0_data_ph = initiator0_data_ph_nq & ~force_bus_idle; +always_comb initiator1_data_ph = initiator1_data_ph_nq & ~force_bus_idle; +always_comb initiator0_pend_addr_ph = initiator0_pend_addr_ph_nq & ~force_bus_idle; +always_comb initiator1_pend_addr_ph = initiator1_pend_addr_ph_nq & ~force_bus_idle; + +always_comb initiator0_haddr = initiator0_pend_addr_ph ? initiator0_pend_haddr : haddr_i_0; +always_comb initiator0_htrans = initiator0_pend_addr_ph ? initiator0_pend_htrans : htrans_i_0; +always_comb initiator0_hsize = initiator0_pend_addr_ph ? initiator0_pend_hsize : hsize_i_0; +always_comb initiator0_hwrite = initiator0_pend_addr_ph ? initiator0_pend_hwrite : hwrite_i_0; + +always_comb initiator1_haddr = initiator1_pend_addr_ph ? initiator1_pend_haddr : haddr_i_1; +always_comb initiator1_htrans = initiator1_pend_addr_ph ? initiator1_pend_htrans : htrans_i_1; +always_comb initiator1_hsize = initiator1_pend_addr_ph ? initiator1_pend_hsize : hsize_i_1; +always_comb initiator1_hwrite = initiator1_pend_addr_ph ? initiator1_pend_hwrite : hwrite_i_1; + +//Select the appropriate initiator +generate + if (AHB_NO_OPT) begin + //no optimization, data phase must complete before driving new address phase + //Initiator 0 gets priority + //Stall the grant only if initiator 1 is on its data phase + always_comb initiator0_gnt = (initiator0_address_ph | initiator0_pend_addr_ph) & ~initiator1_data_ph; + + //Initiator 1 gets through only if initiator 0 address phase isn't getting gnt, or in data phase + always_comb initiator1_gnt = (initiator1_address_ph | initiator1_pend_addr_ph) & ~initiator0_data_ph & ~initiator0_gnt; + end else begin + //optimized to allow addr phase to overlap data phase, assumes no stalls + //Initiator 0 gets priority + //Stall the grant if initiator 1 is processing a data phase and address phase b2b + always_comb initiator0_gnt = (initiator0_address_ph | initiator0_pend_addr_ph); + + //Initiator 1 gets through only if initiator 0 isn't getting granted + always_comb initiator1_gnt = (initiator1_address_ph | initiator1_pend_addr_ph) & ~initiator0_gnt; + end +endgenerate + +//Mux the appropriate initiator and send out +//Keep driving initiator 1 controls on data phase if init0 isn't getting a grant in that cycle +always_comb haddr_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? initiator1_haddr : initiator0_haddr; +always_comb htrans_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? initiator1_htrans : initiator0_htrans; +always_comb hsize_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? initiator1_hsize : initiator0_hsize; +always_comb hwrite_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? initiator1_hwrite : initiator0_hwrite; +always_comb hsel_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? hsel_i_1 : hsel_i_0; +always_comb hwdata_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? hwdata_i_1 : hwdata_i_0; +always_comb hready_o = initiator1_gnt | (initiator1_data_ph & ~initiator0_gnt) ? (hready_i_1 | initiator1_pend_addr_ph) : (hready_i_0 | initiator0_pend_addr_ph); + +//Send response to the initiator +//Mask the ready when it's a pending address phase +//Send the data coming from responder when selected +always_comb hresp_o_0 = initiator0_data_ph ? hresp_i : '0; +always_comb hrdata_o_0 = initiator0_data_ph ? hrdata_i : '0; +always_comb hready_o_0 = initiator0_data_ph ? hreadyout_i : + initiator0_pend_addr_ph ? '0 : '1; + +always_comb hresp_o_1 = initiator1_data_ph? hresp_i: '0; +always_comb hrdata_o_1 = initiator1_data_ph ? hrdata_i: '0; +always_comb hready_o_1 = initiator1_data_ph ? hreadyout_i : + initiator1_pend_addr_ph ? '0 : '1; + +//Coverage +`ifndef VERILATOR +`ifdef FCOV + +covergroup ahb_lite_2to1_mux_cov_grp @(posedge hclk iff hreset_n); + option.per_instance = 1; + + init0_addr_cp: coverpoint initiator0_address_ph; + init0_pend_addr_cp: coverpoint initiator0_pend_addr_ph; + init0_data_cp: coverpoint initiator0_data_ph; + init0_gnt_cp : coverpoint initiator0_gnt; + + init1_addr_cp: coverpoint initiator1_address_ph; + init1_pend_addr_cp: coverpoint initiator1_pend_addr_ph; + init1_data_cp: coverpoint initiator1_data_ph; + init1_gnt_cp : coverpoint initiator1_gnt; + + init0_pend_addr_not_ready: coverpoint initiator0_pend_addr_ph & ~hreadyout_i; + init1_pend_addr_not_ready: coverpoint initiator1_pend_addr_ph & ~hreadyout_i; + + init0_data_not_ready: coverpoint initiator0_data_ph & ~hreadyout_i; + init1_data_not_ready: coverpoint initiator1_data_ph & ~hreadyout_i; + + init0_dataXinit1_gnt: cross init0_data_cp, init1_gnt_cp; + init1_dataXinit0_gnt: cross init1_data_cp, init0_gnt_cp; + + init0_addrXpend: cross init0_addr_cp, init0_pend_addr_cp; + init1_addrXpend: cross init1_addr_cp, init1_pend_addr_cp; + init0Xinit1_addr: cross init0_addr_cp, init1_addr_cp; + init0Xinit1_pend_addr: cross init0_pend_addr_cp, init1_pend_addr_cp; + +endgroup + + ahb_lite_2to1_mux_cov_grp ahb_lite_2to1_mux_cov_grp1 = new(); + +`endif +`endif +endmodule diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index aef0fa73fe3..e09d071a6ca 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -74,6 +74,20 @@ module tb_top logic lsu_hready ; logic lsu_hresp ; + logic [31:0] mux_haddr ; + logic [2:0] mux_hburst ; + logic mux_hmastlock ; + logic [3:0] mux_hprot ; + logic [2:0] mux_hsize ; + logic [1:0] mux_htrans ; + logic mux_hwrite ; + logic mux_hsel ; + logic [63:0] mux_hrdata ; + logic [63:0] mux_hwdata ; + logic mux_hready ; + logic mux_hresp ; + logic mux_hreadyout ; + logic [31:0] sb_haddr ; logic [2:0] sb_hburst ; logic sb_hmastlock ; @@ -137,6 +151,52 @@ module tb_top logic [11:0] wb_csr_dest; logic [31:0] wb_csr_data; + // SB and LSU AHB master mux + ahb_lite_2to1_mux #( + .AHB_LITE_ADDR_WIDTH (32), + .AHB_LITE_DATA_WIDTH (64), + .AHB_NO_OPT(1) //Prevent address and data phase overlap between initiators + ) u_sb_lsu_ahb_mux ( + .hclk (core_clk), + .hreset_n (rst_l), + .force_bus_idle (), + // Initiator 0 + .hsel_i_0 (1'b1 ), + .haddr_i_0 (lsu_haddr ), + .hwdata_i_0 (lsu_hwdata), + .hwrite_i_0 (lsu_hwrite), + .htrans_i_0 (lsu_htrans), + .hsize_i_0 (lsu_hsize ), + .hready_i_0 (lsu_hready), + .hresp_o_0 (lsu_hresp ), + .hready_o_0 (lsu_hready), + .hrdata_o_0 (lsu_hrdata), + + // Initiator 1 + .hsel_i_1 (1'b1 ), + .haddr_i_1 (sb_haddr ), + .hwdata_i_1 (sb_hwdata ), + .hwrite_i_1 (sb_hwrite ), + .htrans_i_1 (sb_htrans ), + .hsize_i_1 (sb_hsize ), + .hready_i_1 (sb_hready ), + .hresp_o_1 (sb_hresp ), + .hready_o_1 (sb_hready ), + .hrdata_o_1 (sb_hrdata ), + + // Responder + .hsel_o (mux_hsel), + .haddr_o (mux_haddr ), + .hwdata_o (mux_hwdata), + .hwrite_o (mux_hwrite), + .htrans_o (mux_htrans), + .hsize_o (mux_hsize ), + .hready_o (mux_hready), + .hresp_i (mux_hresp ), + .hreadyout_i (mux_hreadyout), + .hrdata_i (mux_hrdata) + ); + `ifdef RV_BUILD_AXI4 //-------------------------- LSU AXI signals-------------------------- // AXI Write Channels @@ -920,22 +980,22 @@ ahb_sif imem ( ahb_sif lmem ( // Inputs - .HWDATA(lsu_hwdata), + .HWDATA(mux_hwdata), .HCLK(core_clk), - .HSEL(1'b1), - .HPROT(lsu_hprot), - .HWRITE(lsu_hwrite), - .HTRANS(lsu_htrans), - .HSIZE(lsu_hsize), - .HREADY(lsu_hready), + .HSEL(mux_hsel), + .HPROT(mux_hprot), + .HWRITE(mux_hwrite), + .HTRANS(mux_htrans), + .HSIZE(mux_hsize), + .HREADY(mux_hready), .HRESETn(rst_l), - .HADDR(lsu_haddr), - .HBURST(lsu_hburst), + .HADDR(mux_haddr), + .HBURST(mux_hburst), // Outputs - .HREADYOUT(lsu_hready), - .HRESP(lsu_hresp), - .HRDATA(lsu_hrdata[63:0]) + .HREADYOUT(mux_hreadyout), + .HRESP(mux_hresp), + .HRDATA(mux_hrdata[63:0]) ); `endif diff --git a/tools/Makefile b/tools/Makefile index cb92229f0a1..44d9d1f6ef7 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -135,7 +135,8 @@ TB_VERILATOR_SRCS = $(TBDIR)/test_tb_top.cpp $(TB_DPI_SRCS) TBFILES = $(TBDIR)/tb_top_pkg.sv \ $(TBDIR)/tb_top.sv \ $(TBDIR)/ahb_sif.sv \ - $(TBDIR)/jtagdpi/jtagdpi.sv + $(TBDIR)/jtagdpi/jtagdpi.sv \ + $(TBDIR)/ahb_lite_2to1_mux.sv defines = $(BUILD_DIR)/common_defines.vh defines += ${RV_ROOT}/design/include/el2_def.sv From f132e01dde908251d29da72ec0302d41bc3174cc Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 28 Jun 2024 13:44:19 +0200 Subject: [PATCH 03/14] Add axi_crossbar Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- testbench/axi4_mux/arbiter.v | 159 +++++ testbench/axi4_mux/axi_crossbar.v | 391 ++++++++++++ testbench/axi4_mux/axi_crossbar_addr.v | 418 +++++++++++++ testbench/axi4_mux/axi_crossbar_rd.v | 569 +++++++++++++++++ testbench/axi4_mux/axi_crossbar_wr.v | 678 ++++++++++++++++++++ testbench/axi4_mux/axi_crossbar_wrap_2x1.v | 440 +++++++++++++ testbench/axi4_mux/axi_register_rd.v | 530 ++++++++++++++++ testbench/axi4_mux/axi_register_wr.v | 691 +++++++++++++++++++++ testbench/axi4_mux/priority_encoder.v | 92 +++ testbench/tb_top.sv | 293 +++++++-- tools/Makefile | 6 +- 11 files changed, 4228 insertions(+), 39 deletions(-) create mode 100644 testbench/axi4_mux/arbiter.v create mode 100644 testbench/axi4_mux/axi_crossbar.v create mode 100644 testbench/axi4_mux/axi_crossbar_addr.v create mode 100644 testbench/axi4_mux/axi_crossbar_rd.v create mode 100644 testbench/axi4_mux/axi_crossbar_wr.v create mode 100644 testbench/axi4_mux/axi_crossbar_wrap_2x1.v create mode 100644 testbench/axi4_mux/axi_register_rd.v create mode 100644 testbench/axi4_mux/axi_register_wr.v create mode 100644 testbench/axi4_mux/priority_encoder.v diff --git a/testbench/axi4_mux/arbiter.v b/testbench/axi4_mux/arbiter.v new file mode 100644 index 00000000000..cfac70d1c6d --- /dev/null +++ b/testbench/axi4_mux/arbiter.v @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2014-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // select round robin arbitration + parameter ARB_TYPE_ROUND_ROBIN = 0, + // blocking arbiter enable + parameter ARB_BLOCK = 0, + // block on acknowledge assert when nonzero, request deassert when 0 + parameter ARB_BLOCK_ACK = 1, + // LSB priority selection + parameter ARB_LSB_HIGH_PRIORITY = 0 +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (ARB_BLOCK && !ARB_BLOCK_ACK && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (ARB_BLOCK && ARB_BLOCK_ACK && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (ARB_TYPE_ROUND_ROBIN) begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (ARB_LSB_HIGH_PRIORITY) begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end else begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (ARB_LSB_HIGH_PRIORITY) begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end else begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_crossbar.v b/testbench/axi4_mux/axi_crossbar.v new file mode 100644 index 00000000000..991d45403a1 --- /dev/null +++ b/testbench/axi4_mux/axi_crossbar.v @@ -0,0 +1,391 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 crossbar + */ +module axi_crossbar # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Number of concurrent unique IDs for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_THREADS = {S_COUNT{32'd2}}, + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd4}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AW_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_W_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_B_REG_TYPE = {S_COUNT{2'd1}}, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AR_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_R_REG_TYPE = {S_COUNT{2'd2}}, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AW_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_W_REG_TYPE = {M_COUNT{2'd2}}, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_B_REG_TYPE = {M_COUNT{2'd0}}, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AR_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_R_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready, + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +axi_crossbar_wr #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ID_WIDTH(S_ID_WIDTH), + .M_ID_WIDTH(M_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .S_THREADS(S_THREADS), + .S_ACCEPT(S_ACCEPT), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT_WRITE), + .M_ISSUE(M_ISSUE), + .M_SECURE(M_SECURE), + .S_AW_REG_TYPE(S_AW_REG_TYPE), + .S_W_REG_TYPE (S_W_REG_TYPE), + .S_B_REG_TYPE (S_B_REG_TYPE) +) +axi_crossbar_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interfaces + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI master interfaces + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awqos(m_axi_awqos), + .m_axi_awregion(m_axi_awregion), + .m_axi_awuser(m_axi_awuser), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wuser(m_axi_wuser), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_buser(m_axi_buser), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready) +); + +axi_crossbar_rd #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ID_WIDTH(S_ID_WIDTH), + .M_ID_WIDTH(M_ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .S_THREADS(S_THREADS), + .S_ACCEPT(S_ACCEPT), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT_READ), + .M_ISSUE(M_ISSUE), + .M_SECURE(M_SECURE), + .S_AR_REG_TYPE(S_AR_REG_TYPE), + .S_R_REG_TYPE (S_R_REG_TYPE) +) +axi_crossbar_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interfaces + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI master interfaces + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arqos(m_axi_arqos), + .m_axi_arregion(m_axi_arregion), + .m_axi_aruser(m_axi_aruser), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_ruser(m_axi_ruser), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready) +); + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_crossbar_addr.v b/testbench/axi4_mux/axi_crossbar_addr.v new file mode 100644 index 00000000000..7b7846526b2 --- /dev/null +++ b/testbench/axi4_mux/axi_crossbar_addr.v @@ -0,0 +1,418 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 crossbar address decode and admission control + */ +module axi_crossbar_addr # +( + // Slave interface index + parameter S = 0, + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // ID field width + parameter ID_WIDTH = 8, + // Number of concurrent unique IDs + parameter S_THREADS = 32'd2, + // Number of concurrent operations + parameter S_ACCEPT = 32'd16, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Enable write command output + parameter WC_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + /* + * Address input + */ + input wire [ID_WIDTH-1:0] s_axi_aid, + input wire [ADDR_WIDTH-1:0] s_axi_aaddr, + input wire [2:0] s_axi_aprot, + input wire [3:0] s_axi_aqos, + input wire s_axi_avalid, + output wire s_axi_aready, + + /* + * Address output + */ + output wire [3:0] m_axi_aregion, + output wire [$clog2(M_COUNT)-1:0] m_select, + output wire m_axi_avalid, + input wire m_axi_aready, + + /* + * Write command output + */ + output wire [$clog2(M_COUNT)-1:0] m_wc_select, + output wire m_wc_decerr, + output wire m_wc_valid, + input wire m_wc_ready, + + /* + * Reply command output + */ + output wire m_rc_decerr, + output wire m_rc_valid, + input wire m_rc_ready, + + /* + * Completion input + */ + input wire [ID_WIDTH-1:0] s_cpl_id, + input wire s_cpl_valid +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +parameter S_INT_THREADS = S_THREADS > S_ACCEPT ? S_ACCEPT : S_THREADS; +parameter CL_S_INT_THREADS = $clog2(S_INT_THREADS); +parameter CL_S_ACCEPT = $clog2(S_ACCEPT); + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + reg [ADDR_WIDTH-1:0] width; + reg [ADDR_WIDTH-1:0] size; + reg [ADDR_WIDTH-1:0] mask; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + width = M_ADDR_WIDTH[i*32 +: 32]; + mask = {ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - width); + size = mask + 1; + if (width > 0) begin + if ((base & mask) != 0) begin + base = base + size - (base & mask); // align + end + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + base = base + size; // increment + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + if (S_ACCEPT < 1) begin + $error("Error: need at least 1 accept (instance %m)"); + $finish; + end + + if (S_THREADS < 1) begin + $error("Error: need at least 1 thread (instance %m)"); + $finish; + end + + if (S_THREADS > S_ACCEPT) begin + $warning("Warning: requested thread count larger than accept count; limiting thread count to accept count (instance %m)"); + end + + if (M_REGIONS < 1) begin + $error("Error: need at least 1 region (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axi_crossbar_addr instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %02d -- %x-%x", + i/M_REGIONS, i%M_REGIONS, + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[i*32 +: 32], + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])) + ); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if ((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & (2**M_ADDR_WIDTH[i*32 +: 32]-1)) != 0) begin + $display("Region not aligned:"); + $display("%2d (%2d): %x / %2d -- %x-%x", + i/M_REGIONS, i%M_REGIONS, + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[i*32 +: 32], + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])) + ); + $error("Error: address range not aligned (instance %m)"); + $finish; + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) + && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", + i/M_REGIONS, i%M_REGIONS, + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[i*32 +: 32], + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])) + ); + $display("%2d (%2d): %x / %2d -- %x-%x", + j/M_REGIONS, j%M_REGIONS, + M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[j*32 +: 32], + M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), + M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])) + ); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg s_axi_aready_reg = 0, s_axi_aready_next; + +reg [3:0] m_axi_aregion_reg = 4'd0, m_axi_aregion_next; +reg [CL_M_COUNT-1:0] m_select_reg = 0, m_select_next; +reg m_axi_avalid_reg = 1'b0, m_axi_avalid_next; +reg m_decerr_reg = 1'b0, m_decerr_next; +reg m_wc_valid_reg = 1'b0, m_wc_valid_next; +reg m_rc_valid_reg = 1'b0, m_rc_valid_next; + +assign s_axi_aready = s_axi_aready_reg; + +assign m_axi_aregion = m_axi_aregion_reg; +assign m_select = m_select_reg; +assign m_axi_avalid = m_axi_avalid_reg; + +assign m_wc_select = m_select_reg; +assign m_wc_decerr = m_decerr_reg; +assign m_wc_valid = m_wc_valid_reg; + +assign m_rc_decerr = m_decerr_reg; +assign m_rc_valid = m_rc_valid_reg; + +reg match; +reg trans_start; +reg trans_complete; + +reg [$clog2(S_ACCEPT+1)-1:0] trans_count_reg = 0; +wire trans_limit = trans_count_reg >= S_ACCEPT && !trans_complete; + +// transfer ID thread tracking +reg [ID_WIDTH-1:0] thread_id_reg[S_INT_THREADS-1:0]; +reg [CL_M_COUNT-1:0] thread_m_reg[S_INT_THREADS-1:0]; +reg [3:0] thread_region_reg[S_INT_THREADS-1:0]; +reg [$clog2(S_ACCEPT+1)-1:0] thread_count_reg[S_INT_THREADS-1:0]; + +wire [S_INT_THREADS-1:0] thread_active; +wire [S_INT_THREADS-1:0] thread_match; +wire [S_INT_THREADS-1:0] thread_match_dest; +wire [S_INT_THREADS-1:0] thread_cpl_match; +wire [S_INT_THREADS-1:0] thread_trans_start; +wire [S_INT_THREADS-1:0] thread_trans_complete; + +generate + genvar n; + + for (n = 0; n < S_INT_THREADS; n = n + 1) begin + initial begin + thread_count_reg[n] <= 0; + end + + assign thread_active[n] = thread_count_reg[n] != 0; + assign thread_match[n] = thread_active[n] && thread_id_reg[n] == s_axi_aid; + assign thread_match_dest[n] = thread_match[n] && thread_m_reg[n] == m_select_next && (M_REGIONS < 2 || thread_region_reg[n] == m_axi_aregion_next); + assign thread_cpl_match[n] = thread_active[n] && thread_id_reg[n] == s_cpl_id; + assign thread_trans_start[n] = (thread_match[n] || (!thread_active[n] && !thread_match && !(thread_trans_start & ({S_INT_THREADS{1'b1}} >> (S_INT_THREADS-n))))) && trans_start; + assign thread_trans_complete[n] = thread_cpl_match[n] && trans_complete; + + always @(posedge clk) begin + if (rst) begin + thread_count_reg[n] <= 0; + end else begin + if (thread_trans_start[n] && !thread_trans_complete[n]) begin + thread_count_reg[n] <= thread_count_reg[n] + 1; + end else if (!thread_trans_start[n] && thread_trans_complete[n]) begin + thread_count_reg[n] <= thread_count_reg[n] - 1; + end + end + + if (thread_trans_start[n]) begin + thread_id_reg[n] <= s_axi_aid; + thread_m_reg[n] <= m_select_next; + thread_region_reg[n] <= m_axi_aregion_next; + end + end + end +endgenerate + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + trans_start = 1'b0; + trans_complete = 1'b0; + + s_axi_aready_next = 1'b0; + + m_axi_aregion_next = m_axi_aregion_reg; + m_select_next = m_select_reg; + m_axi_avalid_next = m_axi_avalid_reg && !m_axi_aready; + m_decerr_next = m_decerr_reg; + m_wc_valid_next = m_wc_valid_reg && !m_wc_ready; + m_rc_valid_next = m_rc_valid_reg && !m_rc_ready; + + case (state_reg) + STATE_IDLE: begin + // idle state, store values + s_axi_aready_next = 1'b0; + + if (s_axi_avalid && !s_axi_aready) begin + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !s_axi_aprot[1]) && (M_CONNECT & (1 << (S+i*S_COUNT))) && (s_axi_aaddr >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + m_axi_aregion_next = j; + match = 1'b1; + end + end + end + + if (match) begin + // address decode successful + if (!trans_limit && (thread_match_dest || (!(&thread_active) && !thread_match))) begin + // transaction limit not reached + m_axi_avalid_next = 1'b1; + m_decerr_next = 1'b0; + m_wc_valid_next = WC_OUTPUT; + m_rc_valid_next = 1'b0; + trans_start = 1'b1; + state_next = STATE_DECODE; + end else begin + // transaction limit reached; block in idle + state_next = STATE_IDLE; + end + end else begin + // decode error + m_axi_avalid_next = 1'b0; + m_decerr_next = 1'b1; + m_wc_valid_next = WC_OUTPUT; + m_rc_valid_next = 1'b1; + state_next = STATE_DECODE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + if (!m_axi_avalid_next && (!m_wc_valid_next || !WC_OUTPUT) && !m_rc_valid_next) begin + s_axi_aready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DECODE; + end + end + endcase + + // manage completions + trans_complete = s_cpl_valid; +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axi_aready_reg <= 1'b0; + m_axi_avalid_reg <= 1'b0; + m_wc_valid_reg <= 1'b0; + m_rc_valid_reg <= 1'b0; + + trans_count_reg <= 0; + end else begin + state_reg <= state_next; + s_axi_aready_reg <= s_axi_aready_next; + m_axi_avalid_reg <= m_axi_avalid_next; + m_wc_valid_reg <= m_wc_valid_next; + m_rc_valid_reg <= m_rc_valid_next; + + if (trans_start && !trans_complete) begin + trans_count_reg <= trans_count_reg + 1; + end else if (!trans_start && trans_complete) begin + trans_count_reg <= trans_count_reg - 1; + end + end + + m_axi_aregion_reg <= m_axi_aregion_next; + m_select_reg <= m_select_next; + m_decerr_reg <= m_decerr_next; +end + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_crossbar_rd.v b/testbench/axi4_mux/axi_crossbar_rd.v new file mode 100644 index 00000000000..2b1410ac626 --- /dev/null +++ b/testbench/axi4_mux/axi_crossbar_rd.v @@ -0,0 +1,569 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 crossbar (read) + */ +module axi_crossbar_rd # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Number of concurrent unique IDs for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_THREADS = {S_COUNT{32'd2}}, + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd4}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AR_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_R_REG_TYPE = {S_COUNT{2'd2}}, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AR_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_R_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); +parameter M_COUNT_P1 = M_COUNT+1; +parameter CL_M_COUNT_P1 = $clog2(M_COUNT_P1); + +integer i; + +// check configuration +initial begin + if (M_ID_WIDTH < S_ID_WIDTH+$clog2(S_COUNT)) begin + $error("Error: M_ID_WIDTH must be at least $clog2(S_COUNT) larger than S_ID_WIDTH (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: value out of range (instance %m)"); + $finish; + end + end +end + +wire [S_COUNT*S_ID_WIDTH-1:0] int_s_axi_arid; +wire [S_COUNT*ADDR_WIDTH-1:0] int_s_axi_araddr; +wire [S_COUNT*8-1:0] int_s_axi_arlen; +wire [S_COUNT*3-1:0] int_s_axi_arsize; +wire [S_COUNT*2-1:0] int_s_axi_arburst; +wire [S_COUNT-1:0] int_s_axi_arlock; +wire [S_COUNT*4-1:0] int_s_axi_arcache; +wire [S_COUNT*3-1:0] int_s_axi_arprot; +wire [S_COUNT*4-1:0] int_s_axi_arqos; +wire [S_COUNT*4-1:0] int_s_axi_arregion; +wire [S_COUNT*ARUSER_WIDTH-1:0] int_s_axi_aruser; +wire [S_COUNT-1:0] int_s_axi_arvalid; +wire [S_COUNT-1:0] int_s_axi_arready; + +wire [S_COUNT*M_COUNT-1:0] int_axi_arvalid; +wire [M_COUNT*S_COUNT-1:0] int_axi_arready; + +wire [M_COUNT*M_ID_WIDTH-1:0] int_m_axi_rid; +wire [M_COUNT*DATA_WIDTH-1:0] int_m_axi_rdata; +wire [M_COUNT*2-1:0] int_m_axi_rresp; +wire [M_COUNT-1:0] int_m_axi_rlast; +wire [M_COUNT*RUSER_WIDTH-1:0] int_m_axi_ruser; +wire [M_COUNT-1:0] int_m_axi_rvalid; +wire [M_COUNT-1:0] int_m_axi_rready; + +wire [M_COUNT*S_COUNT-1:0] int_axi_rvalid; +wire [S_COUNT*M_COUNT-1:0] int_axi_rready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + // address decode and admission control + wire [CL_M_COUNT-1:0] a_select; + + wire m_axi_avalid; + wire m_axi_aready; + + wire m_rc_decerr; + wire m_rc_valid; + wire m_rc_ready; + + wire [S_ID_WIDTH-1:0] s_cpl_id; + wire s_cpl_valid; + + axi_crossbar_addr #( + .S(m), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .ADDR_WIDTH(ADDR_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .S_THREADS(S_THREADS[m*32 +: 32]), + .S_ACCEPT(S_ACCEPT[m*32 +: 32]), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT), + .M_SECURE(M_SECURE), + .WC_OUTPUT(0) + ) + addr_inst ( + .clk(clk), + .rst(rst), + + /* + * Address input + */ + .s_axi_aid(int_s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_aaddr(int_s_axi_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_aprot(int_s_axi_arprot[m*3 +: 3]), + .s_axi_aqos(int_s_axi_arqos[m*4 +: 4]), + .s_axi_avalid(int_s_axi_arvalid[m]), + .s_axi_aready(int_s_axi_arready[m]), + + /* + * Address output + */ + .m_axi_aregion(int_s_axi_arregion[m*4 +: 4]), + .m_select(a_select), + .m_axi_avalid(m_axi_avalid), + .m_axi_aready(m_axi_aready), + + /* + * Write command output + */ + .m_wc_select(), + .m_wc_decerr(), + .m_wc_valid(), + .m_wc_ready(1'b1), + + /* + * Response command output + */ + .m_rc_decerr(m_rc_decerr), + .m_rc_valid(m_rc_valid), + .m_rc_ready(m_rc_ready), + + /* + * Completion input + */ + .s_cpl_id(s_cpl_id), + .s_cpl_valid(s_cpl_valid) + ); + + assign int_axi_arvalid[m*M_COUNT +: M_COUNT] = m_axi_avalid << a_select; + assign m_axi_aready = int_axi_arready[a_select*S_COUNT+m]; + + // decode error handling + reg [S_ID_WIDTH-1:0] decerr_m_axi_rid_reg = {S_ID_WIDTH{1'b0}}, decerr_m_axi_rid_next; + reg decerr_m_axi_rlast_reg = 1'b0, decerr_m_axi_rlast_next; + reg decerr_m_axi_rvalid_reg = 1'b0, decerr_m_axi_rvalid_next; + wire decerr_m_axi_rready; + + reg [7:0] decerr_len_reg = 8'd0, decerr_len_next; + + assign m_rc_ready = !decerr_m_axi_rvalid_reg; + + always @* begin + decerr_len_next = decerr_len_reg; + decerr_m_axi_rid_next = decerr_m_axi_rid_reg; + decerr_m_axi_rlast_next = decerr_m_axi_rlast_reg; + decerr_m_axi_rvalid_next = decerr_m_axi_rvalid_reg; + + if (decerr_m_axi_rvalid_reg) begin + if (decerr_m_axi_rready) begin + if (decerr_len_reg > 0) begin + decerr_len_next = decerr_len_reg-1; + decerr_m_axi_rlast_next = (decerr_len_next == 0); + decerr_m_axi_rvalid_next = 1'b1; + end else begin + decerr_m_axi_rvalid_next = 1'b0; + end + end + end else if (m_rc_valid && m_rc_ready) begin + decerr_len_next = int_s_axi_arlen[m*8 +: 8]; + decerr_m_axi_rid_next = int_s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]; + decerr_m_axi_rlast_next = (decerr_len_next == 0); + decerr_m_axi_rvalid_next = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + decerr_m_axi_rvalid_reg <= 1'b0; + end else begin + decerr_m_axi_rvalid_reg <= decerr_m_axi_rvalid_next; + end + + decerr_m_axi_rid_reg <= decerr_m_axi_rid_next; + decerr_m_axi_rlast_reg <= decerr_m_axi_rlast_next; + decerr_len_reg <= decerr_len_next; + end + + // read response arbitration + wire [M_COUNT_P1-1:0] r_request; + wire [M_COUNT_P1-1:0] r_acknowledge; + wire [M_COUNT_P1-1:0] r_grant; + wire r_grant_valid; + wire [CL_M_COUNT_P1-1:0] r_grant_encoded; + + arbiter #( + .PORTS(M_COUNT_P1), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) + ) + r_arb_inst ( + .clk(clk), + .rst(rst), + .request(r_request), + .acknowledge(r_acknowledge), + .grant(r_grant), + .grant_valid(r_grant_valid), + .grant_encoded(r_grant_encoded) + ); + + // read response mux + wire [S_ID_WIDTH-1:0] m_axi_rid_mux = {decerr_m_axi_rid_reg, int_m_axi_rid} >> r_grant_encoded*M_ID_WIDTH; + wire [DATA_WIDTH-1:0] m_axi_rdata_mux = {{DATA_WIDTH{1'b0}}, int_m_axi_rdata} >> r_grant_encoded*DATA_WIDTH; + wire [1:0] m_axi_rresp_mux = {2'b11, int_m_axi_rresp} >> r_grant_encoded*2; + wire m_axi_rlast_mux = {decerr_m_axi_rlast_reg, int_m_axi_rlast} >> r_grant_encoded; + wire [RUSER_WIDTH-1:0] m_axi_ruser_mux = {{RUSER_WIDTH{1'b0}}, int_m_axi_ruser} >> r_grant_encoded*RUSER_WIDTH; + wire m_axi_rvalid_mux = ({decerr_m_axi_rvalid_reg, int_m_axi_rvalid} >> r_grant_encoded) & r_grant_valid; + wire m_axi_rready_mux; + + assign int_axi_rready[m*M_COUNT +: M_COUNT] = (r_grant_valid && m_axi_rready_mux) << r_grant_encoded; + assign decerr_m_axi_rready = (r_grant_valid && m_axi_rready_mux) && (r_grant_encoded == M_COUNT_P1-1); + + for (n = 0; n < M_COUNT; n = n + 1) begin + assign r_request[n] = int_axi_rvalid[n*S_COUNT+m] && !r_grant[n]; + assign r_acknowledge[n] = r_grant[n] && int_axi_rvalid[n*S_COUNT+m] && m_axi_rlast_mux && m_axi_rready_mux; + end + + assign r_request[M_COUNT_P1-1] = decerr_m_axi_rvalid_reg && !r_grant[M_COUNT_P1-1]; + assign r_acknowledge[M_COUNT_P1-1] = r_grant[M_COUNT_P1-1] && decerr_m_axi_rvalid_reg && decerr_m_axi_rlast_reg && m_axi_rready_mux; + + assign s_cpl_id = m_axi_rid_mux; + assign s_cpl_valid = m_axi_rvalid_mux && m_axi_rready_mux && m_axi_rlast_mux; + + // S side register + axi_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .AR_REG_TYPE(S_AR_REG_TYPE[m*2 +: 2]), + .R_REG_TYPE(S_R_REG_TYPE[m*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_arid(s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_araddr(s_axi_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_arlen(s_axi_arlen[m*8 +: 8]), + .s_axi_arsize(s_axi_arsize[m*3 +: 3]), + .s_axi_arburst(s_axi_arburst[m*2 +: 2]), + .s_axi_arlock(s_axi_arlock[m]), + .s_axi_arcache(s_axi_arcache[m*4 +: 4]), + .s_axi_arprot(s_axi_arprot[m*3 +: 3]), + .s_axi_arqos(s_axi_arqos[m*4 +: 4]), + .s_axi_arregion(4'd0), + .s_axi_aruser(s_axi_aruser[m*ARUSER_WIDTH +: ARUSER_WIDTH]), + .s_axi_arvalid(s_axi_arvalid[m]), + .s_axi_arready(s_axi_arready[m]), + .s_axi_rid(s_axi_rid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_rdata(s_axi_rdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axi_rresp(s_axi_rresp[m*2 +: 2]), + .s_axi_rlast(s_axi_rlast[m]), + .s_axi_ruser(s_axi_ruser[m*RUSER_WIDTH +: RUSER_WIDTH]), + .s_axi_rvalid(s_axi_rvalid[m]), + .s_axi_rready(s_axi_rready[m]), + .m_axi_arid(int_s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .m_axi_araddr(int_s_axi_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_arlen(int_s_axi_arlen[m*8 +: 8]), + .m_axi_arsize(int_s_axi_arsize[m*3 +: 3]), + .m_axi_arburst(int_s_axi_arburst[m*2 +: 2]), + .m_axi_arlock(int_s_axi_arlock[m]), + .m_axi_arcache(int_s_axi_arcache[m*4 +: 4]), + .m_axi_arprot(int_s_axi_arprot[m*3 +: 3]), + .m_axi_arqos(int_s_axi_arqos[m*4 +: 4]), + .m_axi_arregion(), + .m_axi_aruser(int_s_axi_aruser[m*ARUSER_WIDTH +: ARUSER_WIDTH]), + .m_axi_arvalid(int_s_axi_arvalid[m]), + .m_axi_arready(int_s_axi_arready[m]), + .m_axi_rid(m_axi_rid_mux), + .m_axi_rdata(m_axi_rdata_mux), + .m_axi_rresp(m_axi_rresp_mux), + .m_axi_rlast(m_axi_rlast_mux), + .m_axi_ruser(m_axi_ruser_mux), + .m_axi_rvalid(m_axi_rvalid_mux), + .m_axi_rready(m_axi_rready_mux) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + // in-flight transaction count + wire trans_start; + wire trans_complete; + reg [$clog2(M_ISSUE[n*32 +: 32]+1)-1:0] trans_count_reg = 0; + + wire trans_limit = trans_count_reg >= M_ISSUE[n*32 +: 32] && !trans_complete; + + always @(posedge clk) begin + if (rst) begin + trans_count_reg <= 0; + end else begin + if (trans_start && !trans_complete) begin + trans_count_reg <= trans_count_reg + 1; + end else if (!trans_start && trans_complete) begin + trans_count_reg <= trans_count_reg - 1; + end + end + end + + // address arbitration + wire [S_COUNT-1:0] a_request; + wire [S_COUNT-1:0] a_acknowledge; + wire [S_COUNT-1:0] a_grant; + wire a_grant_valid; + wire [CL_S_COUNT-1:0] a_grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) + ) + a_arb_inst ( + .clk(clk), + .rst(rst), + .request(a_request), + .acknowledge(a_acknowledge), + .grant(a_grant), + .grant_valid(a_grant_valid), + .grant_encoded(a_grant_encoded) + ); + + // address mux + wire [M_ID_WIDTH-1:0] s_axi_arid_mux = int_s_axi_arid[a_grant_encoded*S_ID_WIDTH +: S_ID_WIDTH] | (a_grant_encoded << S_ID_WIDTH); + wire [ADDR_WIDTH-1:0] s_axi_araddr_mux = int_s_axi_araddr[a_grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [7:0] s_axi_arlen_mux = int_s_axi_arlen[a_grant_encoded*8 +: 8]; + wire [2:0] s_axi_arsize_mux = int_s_axi_arsize[a_grant_encoded*3 +: 3]; + wire [1:0] s_axi_arburst_mux = int_s_axi_arburst[a_grant_encoded*2 +: 2]; + wire s_axi_arlock_mux = int_s_axi_arlock[a_grant_encoded]; + wire [3:0] s_axi_arcache_mux = int_s_axi_arcache[a_grant_encoded*4 +: 4]; + wire [2:0] s_axi_arprot_mux = int_s_axi_arprot[a_grant_encoded*3 +: 3]; + wire [3:0] s_axi_arqos_mux = int_s_axi_arqos[a_grant_encoded*4 +: 4]; + wire [3:0] s_axi_arregion_mux = int_s_axi_arregion[a_grant_encoded*4 +: 4]; + wire [ARUSER_WIDTH-1:0] s_axi_aruser_mux = int_s_axi_aruser[a_grant_encoded*ARUSER_WIDTH +: ARUSER_WIDTH]; + wire s_axi_arvalid_mux = int_axi_arvalid[a_grant_encoded*M_COUNT+n] && a_grant_valid; + wire s_axi_arready_mux; + + assign int_axi_arready[n*S_COUNT +: S_COUNT] = (a_grant_valid && s_axi_arready_mux) << a_grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign a_request[m] = int_axi_arvalid[m*M_COUNT+n] && !a_grant[m] && !trans_limit; + assign a_acknowledge[m] = a_grant[m] && int_axi_arvalid[m*M_COUNT+n] && s_axi_arready_mux; + end + + assign trans_start = s_axi_arvalid_mux && s_axi_arready_mux && a_grant_valid; + + // read response forwarding + wire [CL_S_COUNT-1:0] r_select = m_axi_rid[n*M_ID_WIDTH +: M_ID_WIDTH] >> S_ID_WIDTH; + + assign int_axi_rvalid[n*S_COUNT +: S_COUNT] = int_m_axi_rvalid[n] << r_select; + assign int_m_axi_rready[n] = int_axi_rready[r_select*M_COUNT+n]; + + assign trans_complete = int_m_axi_rvalid[n] && int_m_axi_rready[n] && int_m_axi_rlast[n]; + + // M side register + axi_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(M_ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .AR_REG_TYPE(M_AR_REG_TYPE[n*2 +: 2]), + .R_REG_TYPE(M_R_REG_TYPE[n*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_arid(s_axi_arid_mux), + .s_axi_araddr(s_axi_araddr_mux), + .s_axi_arlen(s_axi_arlen_mux), + .s_axi_arsize(s_axi_arsize_mux), + .s_axi_arburst(s_axi_arburst_mux), + .s_axi_arlock(s_axi_arlock_mux), + .s_axi_arcache(s_axi_arcache_mux), + .s_axi_arprot(s_axi_arprot_mux), + .s_axi_arqos(s_axi_arqos_mux), + .s_axi_arregion(s_axi_arregion_mux), + .s_axi_aruser(s_axi_aruser_mux), + .s_axi_arvalid(s_axi_arvalid_mux), + .s_axi_arready(s_axi_arready_mux), + .s_axi_rid(int_m_axi_rid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .s_axi_rdata(int_m_axi_rdata[n*DATA_WIDTH +: DATA_WIDTH]), + .s_axi_rresp(int_m_axi_rresp[n*2 +: 2]), + .s_axi_rlast(int_m_axi_rlast[n]), + .s_axi_ruser(int_m_axi_ruser[n*RUSER_WIDTH +: RUSER_WIDTH]), + .s_axi_rvalid(int_m_axi_rvalid[n]), + .s_axi_rready(int_m_axi_rready[n]), + .m_axi_arid(m_axi_arid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_araddr(m_axi_araddr[n*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_arlen(m_axi_arlen[n*8 +: 8]), + .m_axi_arsize(m_axi_arsize[n*3 +: 3]), + .m_axi_arburst(m_axi_arburst[n*2 +: 2]), + .m_axi_arlock(m_axi_arlock[n]), + .m_axi_arcache(m_axi_arcache[n*4 +: 4]), + .m_axi_arprot(m_axi_arprot[n*3 +: 3]), + .m_axi_arqos(m_axi_arqos[n*4 +: 4]), + .m_axi_arregion(m_axi_arregion[n*4 +: 4]), + .m_axi_aruser(m_axi_aruser[n*ARUSER_WIDTH +: ARUSER_WIDTH]), + .m_axi_arvalid(m_axi_arvalid[n]), + .m_axi_arready(m_axi_arready[n]), + .m_axi_rid(m_axi_rid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_rdata(m_axi_rdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axi_rresp(m_axi_rresp[n*2 +: 2]), + .m_axi_rlast(m_axi_rlast[n]), + .m_axi_ruser(m_axi_ruser[n*RUSER_WIDTH +: RUSER_WIDTH]), + .m_axi_rvalid(m_axi_rvalid[n]), + .m_axi_rready(m_axi_rready[n]) + ); + end // m_ifaces + +endgenerate + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_crossbar_wr.v b/testbench/axi4_mux/axi_crossbar_wr.v new file mode 100644 index 00000000000..5f556653517 --- /dev/null +++ b/testbench/axi4_mux/axi_crossbar_wr.v @@ -0,0 +1,678 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 crossbar (write) + */ +module axi_crossbar_wr # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Number of concurrent unique IDs for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_THREADS = {S_COUNT{32'd2}}, + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd4}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AW_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_W_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_B_REG_TYPE = {S_COUNT{2'd1}}, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AW_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_W_REG_TYPE = {M_COUNT{2'd2}}, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_B_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); +parameter M_COUNT_P1 = M_COUNT+1; +parameter CL_M_COUNT_P1 = $clog2(M_COUNT_P1); + +integer i; + +// check configuration +initial begin + if (M_ID_WIDTH < S_ID_WIDTH+$clog2(S_COUNT)) begin + $error("Error: M_ID_WIDTH must be at least $clog2(S_COUNT) larger than S_ID_WIDTH (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: value out of range (instance %m)"); + $finish; + end + end +end + +wire [S_COUNT*S_ID_WIDTH-1:0] int_s_axi_awid; +wire [S_COUNT*ADDR_WIDTH-1:0] int_s_axi_awaddr; +wire [S_COUNT*8-1:0] int_s_axi_awlen; +wire [S_COUNT*3-1:0] int_s_axi_awsize; +wire [S_COUNT*2-1:0] int_s_axi_awburst; +wire [S_COUNT-1:0] int_s_axi_awlock; +wire [S_COUNT*4-1:0] int_s_axi_awcache; +wire [S_COUNT*3-1:0] int_s_axi_awprot; +wire [S_COUNT*4-1:0] int_s_axi_awqos; +wire [S_COUNT*4-1:0] int_s_axi_awregion; +wire [S_COUNT*AWUSER_WIDTH-1:0] int_s_axi_awuser; +wire [S_COUNT-1:0] int_s_axi_awvalid; +wire [S_COUNT-1:0] int_s_axi_awready; + +wire [S_COUNT*M_COUNT-1:0] int_axi_awvalid; +wire [M_COUNT*S_COUNT-1:0] int_axi_awready; + +wire [S_COUNT*DATA_WIDTH-1:0] int_s_axi_wdata; +wire [S_COUNT*STRB_WIDTH-1:0] int_s_axi_wstrb; +wire [S_COUNT-1:0] int_s_axi_wlast; +wire [S_COUNT*WUSER_WIDTH-1:0] int_s_axi_wuser; +wire [S_COUNT-1:0] int_s_axi_wvalid; +wire [S_COUNT-1:0] int_s_axi_wready; + +wire [S_COUNT*M_COUNT-1:0] int_axi_wvalid; +wire [M_COUNT*S_COUNT-1:0] int_axi_wready; + +wire [M_COUNT*M_ID_WIDTH-1:0] int_m_axi_bid; +wire [M_COUNT*2-1:0] int_m_axi_bresp; +wire [M_COUNT*BUSER_WIDTH-1:0] int_m_axi_buser; +wire [M_COUNT-1:0] int_m_axi_bvalid; +wire [M_COUNT-1:0] int_m_axi_bready; + +wire [M_COUNT*S_COUNT-1:0] int_axi_bvalid; +wire [S_COUNT*M_COUNT-1:0] int_axi_bready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + // address decode and admission control + wire [CL_M_COUNT-1:0] a_select; + + wire m_axi_avalid; + wire m_axi_aready; + + wire [CL_M_COUNT-1:0] m_wc_select; + wire m_wc_decerr; + wire m_wc_valid; + wire m_wc_ready; + + wire m_rc_decerr; + wire m_rc_valid; + wire m_rc_ready; + + wire [S_ID_WIDTH-1:0] s_cpl_id; + wire s_cpl_valid; + + axi_crossbar_addr #( + .S(m), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .ADDR_WIDTH(ADDR_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .S_THREADS(S_THREADS[m*32 +: 32]), + .S_ACCEPT(S_ACCEPT[m*32 +: 32]), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT), + .M_SECURE(M_SECURE), + .WC_OUTPUT(1) + ) + addr_inst ( + .clk(clk), + .rst(rst), + + /* + * Address input + */ + .s_axi_aid(int_s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_aaddr(int_s_axi_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_aprot(int_s_axi_awprot[m*3 +: 3]), + .s_axi_aqos(int_s_axi_awqos[m*4 +: 4]), + .s_axi_avalid(int_s_axi_awvalid[m]), + .s_axi_aready(int_s_axi_awready[m]), + + /* + * Address output + */ + .m_axi_aregion(int_s_axi_awregion[m*4 +: 4]), + .m_select(a_select), + .m_axi_avalid(m_axi_avalid), + .m_axi_aready(m_axi_aready), + + /* + * Write command output + */ + .m_wc_select(m_wc_select), + .m_wc_decerr(m_wc_decerr), + .m_wc_valid(m_wc_valid), + .m_wc_ready(m_wc_ready), + + /* + * Response command output + */ + .m_rc_decerr(m_rc_decerr), + .m_rc_valid(m_rc_valid), + .m_rc_ready(m_rc_ready), + + /* + * Completion input + */ + .s_cpl_id(s_cpl_id), + .s_cpl_valid(s_cpl_valid) + ); + + assign int_axi_awvalid[m*M_COUNT +: M_COUNT] = m_axi_avalid << a_select; + assign m_axi_aready = int_axi_awready[a_select*S_COUNT+m]; + + // write command handling + reg [CL_M_COUNT-1:0] w_select_reg = 0, w_select_next; + reg w_drop_reg = 1'b0, w_drop_next; + reg w_select_valid_reg = 1'b0, w_select_valid_next; + + assign m_wc_ready = !w_select_valid_reg; + + always @* begin + w_select_next = w_select_reg; + w_drop_next = w_drop_reg && !(int_s_axi_wvalid[m] && int_s_axi_wready[m] && int_s_axi_wlast[m]); + w_select_valid_next = w_select_valid_reg && !(int_s_axi_wvalid[m] && int_s_axi_wready[m] && int_s_axi_wlast[m]); + + if (m_wc_valid && !w_select_valid_reg) begin + w_select_next = m_wc_select; + w_drop_next = m_wc_decerr; + w_select_valid_next = m_wc_valid; + end + end + + always @(posedge clk) begin + if (rst) begin + w_select_valid_reg <= 1'b0; + end else begin + w_select_valid_reg <= w_select_valid_next; + end + + w_select_reg <= w_select_next; + w_drop_reg <= w_drop_next; + end + + // write data forwarding + assign int_axi_wvalid[m*M_COUNT +: M_COUNT] = (int_s_axi_wvalid[m] && w_select_valid_reg && !w_drop_reg) << w_select_reg; + assign int_s_axi_wready[m] = int_axi_wready[w_select_reg*S_COUNT+m] || w_drop_reg; + + // decode error handling + reg [S_ID_WIDTH-1:0] decerr_m_axi_bid_reg = {S_ID_WIDTH{1'b0}}, decerr_m_axi_bid_next; + reg decerr_m_axi_bvalid_reg = 1'b0, decerr_m_axi_bvalid_next; + wire decerr_m_axi_bready; + + assign m_rc_ready = !decerr_m_axi_bvalid_reg; + + always @* begin + decerr_m_axi_bid_next = decerr_m_axi_bid_reg; + decerr_m_axi_bvalid_next = decerr_m_axi_bvalid_reg; + + if (decerr_m_axi_bvalid_reg) begin + if (decerr_m_axi_bready) begin + decerr_m_axi_bvalid_next = 1'b0; + end + end else if (m_rc_valid && m_rc_ready) begin + decerr_m_axi_bid_next = int_s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]; + decerr_m_axi_bvalid_next = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + decerr_m_axi_bvalid_reg <= 1'b0; + end else begin + decerr_m_axi_bvalid_reg <= decerr_m_axi_bvalid_next; + end + + decerr_m_axi_bid_reg <= decerr_m_axi_bid_next; + end + + // write response arbitration + wire [M_COUNT_P1-1:0] b_request; + wire [M_COUNT_P1-1:0] b_acknowledge; + wire [M_COUNT_P1-1:0] b_grant; + wire b_grant_valid; + wire [CL_M_COUNT_P1-1:0] b_grant_encoded; + + arbiter #( + .PORTS(M_COUNT_P1), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) + ) + b_arb_inst ( + .clk(clk), + .rst(rst), + .request(b_request), + .acknowledge(b_acknowledge), + .grant(b_grant), + .grant_valid(b_grant_valid), + .grant_encoded(b_grant_encoded) + ); + + // write response mux + wire [S_ID_WIDTH-1:0] m_axi_bid_mux = {decerr_m_axi_bid_reg, int_m_axi_bid} >> b_grant_encoded*M_ID_WIDTH; + wire [1:0] m_axi_bresp_mux = {2'b11, int_m_axi_bresp} >> b_grant_encoded*2; + wire [BUSER_WIDTH-1:0] m_axi_buser_mux = {{BUSER_WIDTH{1'b0}}, int_m_axi_buser} >> b_grant_encoded*BUSER_WIDTH; + wire m_axi_bvalid_mux = ({decerr_m_axi_bvalid_reg, int_m_axi_bvalid} >> b_grant_encoded) & b_grant_valid; + wire m_axi_bready_mux; + + assign int_axi_bready[m*M_COUNT +: M_COUNT] = (b_grant_valid && m_axi_bready_mux) << b_grant_encoded; + assign decerr_m_axi_bready = (b_grant_valid && m_axi_bready_mux) && (b_grant_encoded == M_COUNT_P1-1); + + for (n = 0; n < M_COUNT; n = n + 1) begin + assign b_request[n] = int_axi_bvalid[n*S_COUNT+m] && !b_grant[n]; + assign b_acknowledge[n] = b_grant[n] && int_axi_bvalid[n*S_COUNT+m] && m_axi_bready_mux; + end + + assign b_request[M_COUNT_P1-1] = decerr_m_axi_bvalid_reg && !b_grant[M_COUNT_P1-1]; + assign b_acknowledge[M_COUNT_P1-1] = b_grant[M_COUNT_P1-1] && decerr_m_axi_bvalid_reg && m_axi_bready_mux; + + assign s_cpl_id = m_axi_bid_mux; + assign s_cpl_valid = m_axi_bvalid_mux && m_axi_bready_mux; + + // S side register + axi_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .AW_REG_TYPE(S_AW_REG_TYPE[m*2 +: 2]), + .W_REG_TYPE(S_W_REG_TYPE[m*2 +: 2]), + .B_REG_TYPE(S_B_REG_TYPE[m*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid(s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_awaddr(s_axi_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_awlen(s_axi_awlen[m*8 +: 8]), + .s_axi_awsize(s_axi_awsize[m*3 +: 3]), + .s_axi_awburst(s_axi_awburst[m*2 +: 2]), + .s_axi_awlock(s_axi_awlock[m]), + .s_axi_awcache(s_axi_awcache[m*4 +: 4]), + .s_axi_awprot(s_axi_awprot[m*3 +: 3]), + .s_axi_awqos(s_axi_awqos[m*4 +: 4]), + .s_axi_awregion(4'd0), + .s_axi_awuser(s_axi_awuser[m*AWUSER_WIDTH +: AWUSER_WIDTH]), + .s_axi_awvalid(s_axi_awvalid[m]), + .s_axi_awready(s_axi_awready[m]), + .s_axi_wdata(s_axi_wdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axi_wstrb(s_axi_wstrb[m*STRB_WIDTH +: STRB_WIDTH]), + .s_axi_wlast(s_axi_wlast[m]), + .s_axi_wuser(s_axi_wuser[m*WUSER_WIDTH +: WUSER_WIDTH]), + .s_axi_wvalid(s_axi_wvalid[m]), + .s_axi_wready(s_axi_wready[m]), + .s_axi_bid(s_axi_bid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_bresp(s_axi_bresp[m*2 +: 2]), + .s_axi_buser(s_axi_buser[m*BUSER_WIDTH +: BUSER_WIDTH]), + .s_axi_bvalid(s_axi_bvalid[m]), + .s_axi_bready(s_axi_bready[m]), + .m_axi_awid(int_s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .m_axi_awaddr(int_s_axi_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_awlen(int_s_axi_awlen[m*8 +: 8]), + .m_axi_awsize(int_s_axi_awsize[m*3 +: 3]), + .m_axi_awburst(int_s_axi_awburst[m*2 +: 2]), + .m_axi_awlock(int_s_axi_awlock[m]), + .m_axi_awcache(int_s_axi_awcache[m*4 +: 4]), + .m_axi_awprot(int_s_axi_awprot[m*3 +: 3]), + .m_axi_awqos(int_s_axi_awqos[m*4 +: 4]), + .m_axi_awregion(), + .m_axi_awuser(int_s_axi_awuser[m*AWUSER_WIDTH +: AWUSER_WIDTH]), + .m_axi_awvalid(int_s_axi_awvalid[m]), + .m_axi_awready(int_s_axi_awready[m]), + .m_axi_wdata(int_s_axi_wdata[m*DATA_WIDTH +: DATA_WIDTH]), + .m_axi_wstrb(int_s_axi_wstrb[m*STRB_WIDTH +: STRB_WIDTH]), + .m_axi_wlast(int_s_axi_wlast[m]), + .m_axi_wuser(int_s_axi_wuser[m*WUSER_WIDTH +: WUSER_WIDTH]), + .m_axi_wvalid(int_s_axi_wvalid[m]), + .m_axi_wready(int_s_axi_wready[m]), + .m_axi_bid(m_axi_bid_mux), + .m_axi_bresp(m_axi_bresp_mux), + .m_axi_buser(m_axi_buser_mux), + .m_axi_bvalid(m_axi_bvalid_mux), + .m_axi_bready(m_axi_bready_mux) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + // in-flight transaction count + wire trans_start; + wire trans_complete; + reg [$clog2(M_ISSUE[n*32 +: 32]+1)-1:0] trans_count_reg = 0; + + wire trans_limit = trans_count_reg >= M_ISSUE[n*32 +: 32] && !trans_complete; + + always @(posedge clk) begin + if (rst) begin + trans_count_reg <= 0; + end else begin + if (trans_start && !trans_complete) begin + trans_count_reg <= trans_count_reg + 1; + end else if (!trans_start && trans_complete) begin + trans_count_reg <= trans_count_reg - 1; + end + end + end + + // address arbitration + reg [CL_S_COUNT-1:0] w_select_reg = 0, w_select_next; + reg w_select_valid_reg = 1'b0, w_select_valid_next; + reg w_select_new_reg = 1'b0, w_select_new_next; + + wire [S_COUNT-1:0] a_request; + wire [S_COUNT-1:0] a_acknowledge; + wire [S_COUNT-1:0] a_grant; + wire a_grant_valid; + wire [CL_S_COUNT-1:0] a_grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) + ) + a_arb_inst ( + .clk(clk), + .rst(rst), + .request(a_request), + .acknowledge(a_acknowledge), + .grant(a_grant), + .grant_valid(a_grant_valid), + .grant_encoded(a_grant_encoded) + ); + + // address mux + wire [M_ID_WIDTH-1:0] s_axi_awid_mux = int_s_axi_awid[a_grant_encoded*S_ID_WIDTH +: S_ID_WIDTH] | (a_grant_encoded << S_ID_WIDTH); + wire [ADDR_WIDTH-1:0] s_axi_awaddr_mux = int_s_axi_awaddr[a_grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [7:0] s_axi_awlen_mux = int_s_axi_awlen[a_grant_encoded*8 +: 8]; + wire [2:0] s_axi_awsize_mux = int_s_axi_awsize[a_grant_encoded*3 +: 3]; + wire [1:0] s_axi_awburst_mux = int_s_axi_awburst[a_grant_encoded*2 +: 2]; + wire s_axi_awlock_mux = int_s_axi_awlock[a_grant_encoded]; + wire [3:0] s_axi_awcache_mux = int_s_axi_awcache[a_grant_encoded*4 +: 4]; + wire [2:0] s_axi_awprot_mux = int_s_axi_awprot[a_grant_encoded*3 +: 3]; + wire [3:0] s_axi_awqos_mux = int_s_axi_awqos[a_grant_encoded*4 +: 4]; + wire [3:0] s_axi_awregion_mux = int_s_axi_awregion[a_grant_encoded*4 +: 4]; + wire [AWUSER_WIDTH-1:0] s_axi_awuser_mux = int_s_axi_awuser[a_grant_encoded*AWUSER_WIDTH +: AWUSER_WIDTH]; + wire s_axi_awvalid_mux = int_axi_awvalid[a_grant_encoded*M_COUNT+n] && a_grant_valid; + wire s_axi_awready_mux; + + assign int_axi_awready[n*S_COUNT +: S_COUNT] = (a_grant_valid && s_axi_awready_mux) << a_grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign a_request[m] = int_axi_awvalid[m*M_COUNT+n] && !a_grant[m] && !trans_limit && !w_select_valid_next; + assign a_acknowledge[m] = a_grant[m] && int_axi_awvalid[m*M_COUNT+n] && s_axi_awready_mux; + end + + assign trans_start = s_axi_awvalid_mux && s_axi_awready_mux && a_grant_valid; + + // write data mux + wire [DATA_WIDTH-1:0] s_axi_wdata_mux = int_s_axi_wdata[w_select_reg*DATA_WIDTH +: DATA_WIDTH]; + wire [STRB_WIDTH-1:0] s_axi_wstrb_mux = int_s_axi_wstrb[w_select_reg*STRB_WIDTH +: STRB_WIDTH]; + wire s_axi_wlast_mux = int_s_axi_wlast[w_select_reg]; + wire [WUSER_WIDTH-1:0] s_axi_wuser_mux = int_s_axi_wuser[w_select_reg*WUSER_WIDTH +: WUSER_WIDTH]; + wire s_axi_wvalid_mux = int_axi_wvalid[w_select_reg*M_COUNT+n] && w_select_valid_reg; + wire s_axi_wready_mux; + + assign int_axi_wready[n*S_COUNT +: S_COUNT] = (w_select_valid_reg && s_axi_wready_mux) << w_select_reg; + + // write data routing + always @* begin + w_select_next = w_select_reg; + w_select_valid_next = w_select_valid_reg && !(s_axi_wvalid_mux && s_axi_wready_mux && s_axi_wlast_mux); + w_select_new_next = w_select_new_reg || !a_grant_valid || a_acknowledge; + + if (a_grant_valid && !w_select_valid_reg && w_select_new_reg) begin + w_select_next = a_grant_encoded; + w_select_valid_next = a_grant_valid; + w_select_new_next = 1'b0; + end + end + + always @(posedge clk) begin + if (rst) begin + w_select_valid_reg <= 1'b0; + w_select_new_reg <= 1'b1; + end else begin + w_select_valid_reg <= w_select_valid_next; + w_select_new_reg <= w_select_new_next; + end + + w_select_reg <= w_select_next; + end + + // write response forwarding + wire [CL_S_COUNT-1:0] b_select = m_axi_bid[n*M_ID_WIDTH +: M_ID_WIDTH] >> S_ID_WIDTH; + + assign int_axi_bvalid[n*S_COUNT +: S_COUNT] = int_m_axi_bvalid[n] << b_select; + assign int_m_axi_bready[n] = int_axi_bready[b_select*M_COUNT+n]; + + assign trans_complete = int_m_axi_bvalid[n] && int_m_axi_bready[n]; + + // M side register + axi_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(M_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .AW_REG_TYPE(M_AW_REG_TYPE[n*2 +: 2]), + .W_REG_TYPE(M_W_REG_TYPE[n*2 +: 2]), + .B_REG_TYPE(M_B_REG_TYPE[n*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid(s_axi_awid_mux), + .s_axi_awaddr(s_axi_awaddr_mux), + .s_axi_awlen(s_axi_awlen_mux), + .s_axi_awsize(s_axi_awsize_mux), + .s_axi_awburst(s_axi_awburst_mux), + .s_axi_awlock(s_axi_awlock_mux), + .s_axi_awcache(s_axi_awcache_mux), + .s_axi_awprot(s_axi_awprot_mux), + .s_axi_awqos(s_axi_awqos_mux), + .s_axi_awregion(s_axi_awregion_mux), + .s_axi_awuser(s_axi_awuser_mux), + .s_axi_awvalid(s_axi_awvalid_mux), + .s_axi_awready(s_axi_awready_mux), + .s_axi_wdata(s_axi_wdata_mux), + .s_axi_wstrb(s_axi_wstrb_mux), + .s_axi_wlast(s_axi_wlast_mux), + .s_axi_wuser(s_axi_wuser_mux), + .s_axi_wvalid(s_axi_wvalid_mux), + .s_axi_wready(s_axi_wready_mux), + .s_axi_bid(int_m_axi_bid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .s_axi_bresp(int_m_axi_bresp[n*2 +: 2]), + .s_axi_buser(int_m_axi_buser[n*BUSER_WIDTH +: BUSER_WIDTH]), + .s_axi_bvalid(int_m_axi_bvalid[n]), + .s_axi_bready(int_m_axi_bready[n]), + .m_axi_awid(m_axi_awid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_awaddr(m_axi_awaddr[n*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_awlen(m_axi_awlen[n*8 +: 8]), + .m_axi_awsize(m_axi_awsize[n*3 +: 3]), + .m_axi_awburst(m_axi_awburst[n*2 +: 2]), + .m_axi_awlock(m_axi_awlock[n]), + .m_axi_awcache(m_axi_awcache[n*4 +: 4]), + .m_axi_awprot(m_axi_awprot[n*3 +: 3]), + .m_axi_awqos(m_axi_awqos[n*4 +: 4]), + .m_axi_awregion(m_axi_awregion[n*4 +: 4]), + .m_axi_awuser(m_axi_awuser[n*AWUSER_WIDTH +: AWUSER_WIDTH]), + .m_axi_awvalid(m_axi_awvalid[n]), + .m_axi_awready(m_axi_awready[n]), + .m_axi_wdata(m_axi_wdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axi_wstrb(m_axi_wstrb[n*STRB_WIDTH +: STRB_WIDTH]), + .m_axi_wlast(m_axi_wlast[n]), + .m_axi_wuser(m_axi_wuser[n*WUSER_WIDTH +: WUSER_WIDTH]), + .m_axi_wvalid(m_axi_wvalid[n]), + .m_axi_wready(m_axi_wready[n]), + .m_axi_bid(m_axi_bid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_bresp(m_axi_bresp[n*2 +: 2]), + .m_axi_buser(m_axi_buser[n*BUSER_WIDTH +: BUSER_WIDTH]), + .m_axi_bvalid(m_axi_bvalid[n]), + .m_axi_bready(m_axi_bready[n]) + ); + end // m_ifaces + +endgenerate + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_crossbar_wrap_2x1.v b/testbench/axi4_mux/axi_crossbar_wrap_2x1.v new file mode 100644 index 00000000000..48a146713c3 --- /dev/null +++ b/testbench/axi4_mux/axi_crossbar_wrap_2x1.v @@ -0,0 +1,440 @@ +/* + +Copyright (c) 2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 2x1 crossbar (wrapper) + */ +module axi_crossbar_wrap_2x1 # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Number of concurrent unique IDs + parameter S00_THREADS = 2, + // Number of concurrent operations + parameter S00_ACCEPT = 16, + // Number of concurrent unique IDs + parameter S01_THREADS = 2, + // Number of concurrent operations + parameter S01_ACCEPT = 16, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_REGIONS concatenated fields of ADDR_WIDTH bits + parameter M00_BASE_ADDR = 0, + // Master interface address widths + // M_REGIONS concatenated fields of 32 bits + parameter M00_ADDR_WIDTH = {M_REGIONS{32'd24}}, + // Read connections between interfaces + // S_COUNT bits + parameter M00_CONNECT_READ = 2'b11, + // Write connections between interfaces + // S_COUNT bits + parameter M00_CONNECT_WRITE = 2'b11, + // Number of concurrent operations for each master interface + parameter M00_ISSUE = 4, + // Secure master (fail operations based on awprot/arprot) + parameter M00_SECURE = 0, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S00_AW_REG_TYPE = 0, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S00_W_REG_TYPE = 0, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S00_B_REG_TYPE = 1, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S00_AR_REG_TYPE = 0, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S00_R_REG_TYPE = 2, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S01_AW_REG_TYPE = 0, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S01_W_REG_TYPE = 0, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S01_B_REG_TYPE = 1, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S01_AR_REG_TYPE = 0, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S01_R_REG_TYPE = 2, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M00_AW_REG_TYPE = 1, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M00_W_REG_TYPE = 2, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M00_B_REG_TYPE = 0, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M00_AR_REG_TYPE = 1, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M00_R_REG_TYPE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [S_ID_WIDTH-1:0] s00_axi_awid, + input wire [ADDR_WIDTH-1:0] s00_axi_awaddr, + input wire [7:0] s00_axi_awlen, + input wire [2:0] s00_axi_awsize, + input wire [1:0] s00_axi_awburst, + input wire s00_axi_awlock, + input wire [3:0] s00_axi_awcache, + input wire [2:0] s00_axi_awprot, + input wire [3:0] s00_axi_awqos, + input wire [AWUSER_WIDTH-1:0] s00_axi_awuser, + input wire s00_axi_awvalid, + output wire s00_axi_awready, + input wire [DATA_WIDTH-1:0] s00_axi_wdata, + input wire [STRB_WIDTH-1:0] s00_axi_wstrb, + input wire s00_axi_wlast, + input wire [WUSER_WIDTH-1:0] s00_axi_wuser, + input wire s00_axi_wvalid, + output wire s00_axi_wready, + output wire [S_ID_WIDTH-1:0] s00_axi_bid, + output wire [1:0] s00_axi_bresp, + output wire [BUSER_WIDTH-1:0] s00_axi_buser, + output wire s00_axi_bvalid, + input wire s00_axi_bready, + input wire [S_ID_WIDTH-1:0] s00_axi_arid, + input wire [ADDR_WIDTH-1:0] s00_axi_araddr, + input wire [7:0] s00_axi_arlen, + input wire [2:0] s00_axi_arsize, + input wire [1:0] s00_axi_arburst, + input wire s00_axi_arlock, + input wire [3:0] s00_axi_arcache, + input wire [2:0] s00_axi_arprot, + input wire [3:0] s00_axi_arqos, + input wire [ARUSER_WIDTH-1:0] s00_axi_aruser, + input wire s00_axi_arvalid, + output wire s00_axi_arready, + output wire [S_ID_WIDTH-1:0] s00_axi_rid, + output wire [DATA_WIDTH-1:0] s00_axi_rdata, + output wire [1:0] s00_axi_rresp, + output wire s00_axi_rlast, + output wire [RUSER_WIDTH-1:0] s00_axi_ruser, + output wire s00_axi_rvalid, + input wire s00_axi_rready, + + input wire [S_ID_WIDTH-1:0] s01_axi_awid, + input wire [ADDR_WIDTH-1:0] s01_axi_awaddr, + input wire [7:0] s01_axi_awlen, + input wire [2:0] s01_axi_awsize, + input wire [1:0] s01_axi_awburst, + input wire s01_axi_awlock, + input wire [3:0] s01_axi_awcache, + input wire [2:0] s01_axi_awprot, + input wire [3:0] s01_axi_awqos, + input wire [AWUSER_WIDTH-1:0] s01_axi_awuser, + input wire s01_axi_awvalid, + output wire s01_axi_awready, + input wire [DATA_WIDTH-1:0] s01_axi_wdata, + input wire [STRB_WIDTH-1:0] s01_axi_wstrb, + input wire s01_axi_wlast, + input wire [WUSER_WIDTH-1:0] s01_axi_wuser, + input wire s01_axi_wvalid, + output wire s01_axi_wready, + output wire [S_ID_WIDTH-1:0] s01_axi_bid, + output wire [1:0] s01_axi_bresp, + output wire [BUSER_WIDTH-1:0] s01_axi_buser, + output wire s01_axi_bvalid, + input wire s01_axi_bready, + input wire [S_ID_WIDTH-1:0] s01_axi_arid, + input wire [ADDR_WIDTH-1:0] s01_axi_araddr, + input wire [7:0] s01_axi_arlen, + input wire [2:0] s01_axi_arsize, + input wire [1:0] s01_axi_arburst, + input wire s01_axi_arlock, + input wire [3:0] s01_axi_arcache, + input wire [2:0] s01_axi_arprot, + input wire [3:0] s01_axi_arqos, + input wire [ARUSER_WIDTH-1:0] s01_axi_aruser, + input wire s01_axi_arvalid, + output wire s01_axi_arready, + output wire [S_ID_WIDTH-1:0] s01_axi_rid, + output wire [DATA_WIDTH-1:0] s01_axi_rdata, + output wire [1:0] s01_axi_rresp, + output wire s01_axi_rlast, + output wire [RUSER_WIDTH-1:0] s01_axi_ruser, + output wire s01_axi_rvalid, + input wire s01_axi_rready, + + /* + * AXI master interface + */ + output wire [M_ID_WIDTH-1:0] m00_axi_awid, + output wire [ADDR_WIDTH-1:0] m00_axi_awaddr, + output wire [7:0] m00_axi_awlen, + output wire [2:0] m00_axi_awsize, + output wire [1:0] m00_axi_awburst, + output wire m00_axi_awlock, + output wire [3:0] m00_axi_awcache, + output wire [2:0] m00_axi_awprot, + output wire [3:0] m00_axi_awqos, + output wire [3:0] m00_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m00_axi_awuser, + output wire m00_axi_awvalid, + input wire m00_axi_awready, + output wire [DATA_WIDTH-1:0] m00_axi_wdata, + output wire [STRB_WIDTH-1:0] m00_axi_wstrb, + output wire m00_axi_wlast, + output wire [WUSER_WIDTH-1:0] m00_axi_wuser, + output wire m00_axi_wvalid, + input wire m00_axi_wready, + input wire [M_ID_WIDTH-1:0] m00_axi_bid, + input wire [1:0] m00_axi_bresp, + input wire [BUSER_WIDTH-1:0] m00_axi_buser, + input wire m00_axi_bvalid, + output wire m00_axi_bready, + output wire [M_ID_WIDTH-1:0] m00_axi_arid, + output wire [ADDR_WIDTH-1:0] m00_axi_araddr, + output wire [7:0] m00_axi_arlen, + output wire [2:0] m00_axi_arsize, + output wire [1:0] m00_axi_arburst, + output wire m00_axi_arlock, + output wire [3:0] m00_axi_arcache, + output wire [2:0] m00_axi_arprot, + output wire [3:0] m00_axi_arqos, + output wire [3:0] m00_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m00_axi_aruser, + output wire m00_axi_arvalid, + input wire m00_axi_arready, + input wire [M_ID_WIDTH-1:0] m00_axi_rid, + input wire [DATA_WIDTH-1:0] m00_axi_rdata, + input wire [1:0] m00_axi_rresp, + input wire m00_axi_rlast, + input wire [RUSER_WIDTH-1:0] m00_axi_ruser, + input wire m00_axi_rvalid, + output wire m00_axi_rready +); + +localparam S_COUNT = 2; +localparam M_COUNT = 1; + +// parameter sizing helpers +function [ADDR_WIDTH*M_REGIONS-1:0] w_a_r(input [ADDR_WIDTH*M_REGIONS-1:0] val); + w_a_r = val; +endfunction + +function [32*M_REGIONS-1:0] w_32_r(input [32*M_REGIONS-1:0] val); + w_32_r = val; +endfunction + +function [S_COUNT-1:0] w_s(input [S_COUNT-1:0] val); + w_s = val; +endfunction + +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +function [1:0] w_2(input [1:0] val); + w_2 = val; +endfunction + +function w_1(input val); + w_1 = val; +endfunction + +axi_crossbar #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ID_WIDTH(S_ID_WIDTH), + .M_ID_WIDTH(M_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .S_THREADS({ w_32(S01_THREADS), w_32(S00_THREADS) }), + .S_ACCEPT({ w_32(S01_ACCEPT), w_32(S00_ACCEPT) }), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR({ w_a_r(M00_BASE_ADDR) }), + .M_ADDR_WIDTH({ w_32_r(M00_ADDR_WIDTH) }), + .M_CONNECT_READ({ w_s(M00_CONNECT_READ) }), + .M_CONNECT_WRITE({ w_s(M00_CONNECT_WRITE) }), + .M_ISSUE({ w_32(M00_ISSUE) }), + .M_SECURE({ w_1(M00_SECURE) }), + .S_AR_REG_TYPE({ w_2(S01_AR_REG_TYPE), w_2(S00_AR_REG_TYPE) }), + .S_R_REG_TYPE({ w_2(S01_R_REG_TYPE), w_2(S00_R_REG_TYPE) }), + .S_AW_REG_TYPE({ w_2(S01_AW_REG_TYPE), w_2(S00_AW_REG_TYPE) }), + .S_W_REG_TYPE({ w_2(S01_W_REG_TYPE), w_2(S00_W_REG_TYPE) }), + .S_B_REG_TYPE({ w_2(S01_B_REG_TYPE), w_2(S00_B_REG_TYPE) }), + .M_AR_REG_TYPE({ w_2(M00_AR_REG_TYPE) }), + .M_R_REG_TYPE({ w_2(M00_R_REG_TYPE) }), + .M_AW_REG_TYPE({ w_2(M00_AW_REG_TYPE) }), + .M_W_REG_TYPE({ w_2(M00_W_REG_TYPE) }), + .M_B_REG_TYPE({ w_2(M00_B_REG_TYPE) }) +) +axi_crossbar_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid({ s01_axi_awid, s00_axi_awid }), + .s_axi_awaddr({ s01_axi_awaddr, s00_axi_awaddr }), + .s_axi_awlen({ s01_axi_awlen, s00_axi_awlen }), + .s_axi_awsize({ s01_axi_awsize, s00_axi_awsize }), + .s_axi_awburst({ s01_axi_awburst, s00_axi_awburst }), + .s_axi_awlock({ s01_axi_awlock, s00_axi_awlock }), + .s_axi_awcache({ s01_axi_awcache, s00_axi_awcache }), + .s_axi_awprot({ s01_axi_awprot, s00_axi_awprot }), + .s_axi_awqos({ s01_axi_awqos, s00_axi_awqos }), + .s_axi_awuser({ s01_axi_awuser, s00_axi_awuser }), + .s_axi_awvalid({ s01_axi_awvalid, s00_axi_awvalid }), + .s_axi_awready({ s01_axi_awready, s00_axi_awready }), + .s_axi_wdata({ s01_axi_wdata, s00_axi_wdata }), + .s_axi_wstrb({ s01_axi_wstrb, s00_axi_wstrb }), + .s_axi_wlast({ s01_axi_wlast, s00_axi_wlast }), + .s_axi_wuser({ s01_axi_wuser, s00_axi_wuser }), + .s_axi_wvalid({ s01_axi_wvalid, s00_axi_wvalid }), + .s_axi_wready({ s01_axi_wready, s00_axi_wready }), + .s_axi_bid({ s01_axi_bid, s00_axi_bid }), + .s_axi_bresp({ s01_axi_bresp, s00_axi_bresp }), + .s_axi_buser({ s01_axi_buser, s00_axi_buser }), + .s_axi_bvalid({ s01_axi_bvalid, s00_axi_bvalid }), + .s_axi_bready({ s01_axi_bready, s00_axi_bready }), + .s_axi_arid({ s01_axi_arid, s00_axi_arid }), + .s_axi_araddr({ s01_axi_araddr, s00_axi_araddr }), + .s_axi_arlen({ s01_axi_arlen, s00_axi_arlen }), + .s_axi_arsize({ s01_axi_arsize, s00_axi_arsize }), + .s_axi_arburst({ s01_axi_arburst, s00_axi_arburst }), + .s_axi_arlock({ s01_axi_arlock, s00_axi_arlock }), + .s_axi_arcache({ s01_axi_arcache, s00_axi_arcache }), + .s_axi_arprot({ s01_axi_arprot, s00_axi_arprot }), + .s_axi_arqos({ s01_axi_arqos, s00_axi_arqos }), + .s_axi_aruser({ s01_axi_aruser, s00_axi_aruser }), + .s_axi_arvalid({ s01_axi_arvalid, s00_axi_arvalid }), + .s_axi_arready({ s01_axi_arready, s00_axi_arready }), + .s_axi_rid({ s01_axi_rid, s00_axi_rid }), + .s_axi_rdata({ s01_axi_rdata, s00_axi_rdata }), + .s_axi_rresp({ s01_axi_rresp, s00_axi_rresp }), + .s_axi_rlast({ s01_axi_rlast, s00_axi_rlast }), + .s_axi_ruser({ s01_axi_ruser, s00_axi_ruser }), + .s_axi_rvalid({ s01_axi_rvalid, s00_axi_rvalid }), + .s_axi_rready({ s01_axi_rready, s00_axi_rready }), + .m_axi_awid({ m00_axi_awid }), + .m_axi_awaddr({ m00_axi_awaddr }), + .m_axi_awlen({ m00_axi_awlen }), + .m_axi_awsize({ m00_axi_awsize }), + .m_axi_awburst({ m00_axi_awburst }), + .m_axi_awlock({ m00_axi_awlock }), + .m_axi_awcache({ m00_axi_awcache }), + .m_axi_awprot({ m00_axi_awprot }), + .m_axi_awqos({ m00_axi_awqos }), + .m_axi_awregion({ m00_axi_awregion }), + .m_axi_awuser({ m00_axi_awuser }), + .m_axi_awvalid({ m00_axi_awvalid }), + .m_axi_awready({ m00_axi_awready }), + .m_axi_wdata({ m00_axi_wdata }), + .m_axi_wstrb({ m00_axi_wstrb }), + .m_axi_wlast({ m00_axi_wlast }), + .m_axi_wuser({ m00_axi_wuser }), + .m_axi_wvalid({ m00_axi_wvalid }), + .m_axi_wready({ m00_axi_wready }), + .m_axi_bid({ m00_axi_bid }), + .m_axi_bresp({ m00_axi_bresp }), + .m_axi_buser({ m00_axi_buser }), + .m_axi_bvalid({ m00_axi_bvalid }), + .m_axi_bready({ m00_axi_bready }), + .m_axi_arid({ m00_axi_arid }), + .m_axi_araddr({ m00_axi_araddr }), + .m_axi_arlen({ m00_axi_arlen }), + .m_axi_arsize({ m00_axi_arsize }), + .m_axi_arburst({ m00_axi_arburst }), + .m_axi_arlock({ m00_axi_arlock }), + .m_axi_arcache({ m00_axi_arcache }), + .m_axi_arprot({ m00_axi_arprot }), + .m_axi_arqos({ m00_axi_arqos }), + .m_axi_arregion({ m00_axi_arregion }), + .m_axi_aruser({ m00_axi_aruser }), + .m_axi_arvalid({ m00_axi_arvalid }), + .m_axi_arready({ m00_axi_arready }), + .m_axi_rid({ m00_axi_rid }), + .m_axi_rdata({ m00_axi_rdata }), + .m_axi_rresp({ m00_axi_rresp }), + .m_axi_rlast({ m00_axi_rlast }), + .m_axi_ruser({ m00_axi_ruser }), + .m_axi_rvalid({ m00_axi_rvalid }), + .m_axi_rready({ m00_axi_rready }) +); + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_register_rd.v b/testbench/axi4_mux/axi_register_rd.v new file mode 100644 index 00000000000..c0df03a03f4 --- /dev/null +++ b/testbench/axi4_mux/axi_register_rd.v @@ -0,0 +1,530 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 register (read) + */ +module axi_register_rd # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter R_REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +generate + +// AR channel + +if (AR_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_arready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_arlen_reg = 8'd0; +reg [2:0] m_axi_arsize_reg = 3'd0; +reg [1:0] m_axi_arburst_reg = 2'd0; +reg m_axi_arlock_reg = 1'b0; +reg [3:0] m_axi_arcache_reg = 4'd0; +reg [2:0] m_axi_arprot_reg = 3'd0; +reg [3:0] m_axi_arqos_reg = 4'd0; +reg [3:0] m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + +reg [ID_WIDTH-1:0] temp_m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] temp_m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] temp_m_axi_arlen_reg = 8'd0; +reg [2:0] temp_m_axi_arsize_reg = 3'd0; +reg [1:0] temp_m_axi_arburst_reg = 2'd0; +reg temp_m_axi_arlock_reg = 1'b0; +reg [3:0] temp_m_axi_arcache_reg = 4'd0; +reg [2:0] temp_m_axi_arprot_reg = 3'd0; +reg [3:0] temp_m_axi_arqos_reg = 4'd0; +reg [3:0] temp_m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] temp_m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg temp_m_axi_arvalid_reg = 1'b0, temp_m_axi_arvalid_next; + +// datapath control +reg store_axi_ar_input_to_output; +reg store_axi_ar_input_to_temp; +reg store_axi_ar_temp_to_output; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = m_axi_arid_reg; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_arready_early = m_axi_arready | (~temp_m_axi_arvalid_reg & (~m_axi_arvalid_reg | ~s_axi_arvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_arvalid_next = m_axi_arvalid_reg; + temp_m_axi_arvalid_next = temp_m_axi_arvalid_reg; + + store_axi_ar_input_to_output = 1'b0; + store_axi_ar_input_to_temp = 1'b0; + store_axi_ar_temp_to_output = 1'b0; + + if (s_axi_arready_reg) begin + // input is ready + if (m_axi_arready | ~m_axi_arvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_temp = 1'b1; + end + end else if (m_axi_arready) begin + // input is not ready, but output is ready + m_axi_arvalid_next = temp_m_axi_arvalid_reg; + temp_m_axi_arvalid_next = 1'b0; + store_axi_ar_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + temp_m_axi_arvalid_reg <= 1'b0; + end else begin + s_axi_arready_reg <= s_axi_arready_early; + m_axi_arvalid_reg <= m_axi_arvalid_next; + temp_m_axi_arvalid_reg <= temp_m_axi_arvalid_next; + end + + // datapath + if (store_axi_ar_input_to_output) begin + m_axi_arid_reg <= s_axi_arid; + m_axi_araddr_reg <= s_axi_araddr; + m_axi_arlen_reg <= s_axi_arlen; + m_axi_arsize_reg <= s_axi_arsize; + m_axi_arburst_reg <= s_axi_arburst; + m_axi_arlock_reg <= s_axi_arlock; + m_axi_arcache_reg <= s_axi_arcache; + m_axi_arprot_reg <= s_axi_arprot; + m_axi_arqos_reg <= s_axi_arqos; + m_axi_arregion_reg <= s_axi_arregion; + m_axi_aruser_reg <= s_axi_aruser; + end else if (store_axi_ar_temp_to_output) begin + m_axi_arid_reg <= temp_m_axi_arid_reg; + m_axi_araddr_reg <= temp_m_axi_araddr_reg; + m_axi_arlen_reg <= temp_m_axi_arlen_reg; + m_axi_arsize_reg <= temp_m_axi_arsize_reg; + m_axi_arburst_reg <= temp_m_axi_arburst_reg; + m_axi_arlock_reg <= temp_m_axi_arlock_reg; + m_axi_arcache_reg <= temp_m_axi_arcache_reg; + m_axi_arprot_reg <= temp_m_axi_arprot_reg; + m_axi_arqos_reg <= temp_m_axi_arqos_reg; + m_axi_arregion_reg <= temp_m_axi_arregion_reg; + m_axi_aruser_reg <= temp_m_axi_aruser_reg; + end + + if (store_axi_ar_input_to_temp) begin + temp_m_axi_arid_reg <= s_axi_arid; + temp_m_axi_araddr_reg <= s_axi_araddr; + temp_m_axi_arlen_reg <= s_axi_arlen; + temp_m_axi_arsize_reg <= s_axi_arsize; + temp_m_axi_arburst_reg <= s_axi_arburst; + temp_m_axi_arlock_reg <= s_axi_arlock; + temp_m_axi_arcache_reg <= s_axi_arcache; + temp_m_axi_arprot_reg <= s_axi_arprot; + temp_m_axi_arqos_reg <= s_axi_arqos; + temp_m_axi_arregion_reg <= s_axi_arregion; + temp_m_axi_aruser_reg <= s_axi_aruser; + end +end + +end else if (AR_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_arready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_arlen_reg = 8'd0; +reg [2:0] m_axi_arsize_reg = 3'd0; +reg [1:0] m_axi_arburst_reg = 2'd0; +reg m_axi_arlock_reg = 1'b0; +reg [3:0] m_axi_arcache_reg = 4'd0; +reg [2:0] m_axi_arprot_reg = 3'd0; +reg [3:0] m_axi_arqos_reg = 4'd0; +reg [3:0] m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + +// datapath control +reg store_axi_ar_input_to_output; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = m_axi_arid_reg; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_arready_early = !m_axi_arvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_arvalid_next = m_axi_arvalid_reg; + + store_axi_ar_input_to_output = 1'b0; + + if (s_axi_arready_reg) begin + m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_output = 1'b1; + end else if (m_axi_arready) begin + m_axi_arvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + end else begin + s_axi_arready_reg <= s_axi_arready_early; + m_axi_arvalid_reg <= m_axi_arvalid_next; + end + + // datapath + if (store_axi_ar_input_to_output) begin + m_axi_arid_reg <= s_axi_arid; + m_axi_araddr_reg <= s_axi_araddr; + m_axi_arlen_reg <= s_axi_arlen; + m_axi_arsize_reg <= s_axi_arsize; + m_axi_arburst_reg <= s_axi_arburst; + m_axi_arlock_reg <= s_axi_arlock; + m_axi_arcache_reg <= s_axi_arcache; + m_axi_arprot_reg <= s_axi_arprot; + m_axi_arqos_reg <= s_axi_arqos; + m_axi_arregion_reg <= s_axi_arregion; + m_axi_aruser_reg <= s_axi_aruser; + end +end + +end else begin + + // bypass AR channel + assign m_axi_arid = s_axi_arid; + assign m_axi_araddr = s_axi_araddr; + assign m_axi_arlen = s_axi_arlen; + assign m_axi_arsize = s_axi_arsize; + assign m_axi_arburst = s_axi_arburst; + assign m_axi_arlock = s_axi_arlock; + assign m_axi_arcache = s_axi_arcache; + assign m_axi_arprot = s_axi_arprot; + assign m_axi_arqos = s_axi_arqos; + assign m_axi_arregion = s_axi_arregion; + assign m_axi_aruser = ARUSER_ENABLE ? s_axi_aruser : {ARUSER_WIDTH{1'b0}}; + assign m_axi_arvalid = s_axi_arvalid; + assign s_axi_arready = m_axi_arready; + +end + +// R channel + +if (R_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axi_rready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'b0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'b0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_input_to_output; +reg store_axi_r_input_to_temp; +reg store_axi_r_temp_to_output; + +assign m_axi_rready = m_axi_rready_reg; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axi_rready_early = s_axi_rready | (~temp_s_axi_rvalid_reg & (~s_axi_rvalid_reg | ~m_axi_rvalid)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_input_to_output = 1'b0; + store_axi_r_input_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (m_axi_rready_reg) begin + // input is ready + if (s_axi_rready | ~s_axi_rvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_temp = 1'b1; + end + end else if (s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_rready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + m_axi_rready_reg <= m_axi_rready_early; + s_axi_rvalid_reg <= s_axi_rvalid_next; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_input_to_output) begin + s_axi_rid_reg <= m_axi_rid; + s_axi_rdata_reg <= m_axi_rdata; + s_axi_rresp_reg <= m_axi_rresp; + s_axi_rlast_reg <= m_axi_rlast; + s_axi_ruser_reg <= m_axi_ruser; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_input_to_temp) begin + temp_s_axi_rid_reg <= m_axi_rid; + temp_s_axi_rdata_reg <= m_axi_rdata; + temp_s_axi_rresp_reg <= m_axi_rresp; + temp_s_axi_rlast_reg <= m_axi_rlast; + temp_s_axi_ruser_reg <= m_axi_ruser; + end +end + +end else if (R_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axi_rready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'b0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +// datapath control +reg store_axi_r_input_to_output; + +assign m_axi_rready = m_axi_rready_reg; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axi_rready_early = !s_axi_rvalid_next; + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + + store_axi_r_input_to_output = 1'b0; + + if (m_axi_rready_reg) begin + s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_output = 1'b1; + end else if (s_axi_rready) begin + s_axi_rvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_rready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + end else begin + m_axi_rready_reg <= m_axi_rready_early; + s_axi_rvalid_reg <= s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_input_to_output) begin + s_axi_rid_reg <= m_axi_rid; + s_axi_rdata_reg <= m_axi_rdata; + s_axi_rresp_reg <= m_axi_rresp; + s_axi_rlast_reg <= m_axi_rlast; + s_axi_ruser_reg <= m_axi_ruser; + end +end + +end else begin + + // bypass R channel + assign s_axi_rid = m_axi_rid; + assign s_axi_rdata = m_axi_rdata; + assign s_axi_rresp = m_axi_rresp; + assign s_axi_rlast = m_axi_rlast; + assign s_axi_ruser = RUSER_ENABLE ? m_axi_ruser : {RUSER_WIDTH{1'b0}}; + assign s_axi_rvalid = m_axi_rvalid; + assign m_axi_rready = s_axi_rready; + +end + +endgenerate + +endmodule + +`resetall diff --git a/testbench/axi4_mux/axi_register_wr.v b/testbench/axi4_mux/axi_register_wr.v new file mode 100644 index 00000000000..9176d6ba956 --- /dev/null +++ b/testbench/axi4_mux/axi_register_wr.v @@ -0,0 +1,691 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 register (write) + */ +module axi_register_wr # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // AW channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter W_REG_TYPE = 2, + // B channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter B_REG_TYPE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready +); + +generate + +// AW channel + +if (AW_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_awready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_awlen_reg = 8'd0; +reg [2:0] m_axi_awsize_reg = 3'd0; +reg [1:0] m_axi_awburst_reg = 2'd0; +reg m_axi_awlock_reg = 1'b0; +reg [3:0] m_axi_awcache_reg = 4'd0; +reg [2:0] m_axi_awprot_reg = 3'd0; +reg [3:0] m_axi_awqos_reg = 4'd0; +reg [3:0] m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +reg [ID_WIDTH-1:0] temp_m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] temp_m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] temp_m_axi_awlen_reg = 8'd0; +reg [2:0] temp_m_axi_awsize_reg = 3'd0; +reg [1:0] temp_m_axi_awburst_reg = 2'd0; +reg temp_m_axi_awlock_reg = 1'b0; +reg [3:0] temp_m_axi_awcache_reg = 4'd0; +reg [2:0] temp_m_axi_awprot_reg = 3'd0; +reg [3:0] temp_m_axi_awqos_reg = 4'd0; +reg [3:0] temp_m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] temp_m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg temp_m_axi_awvalid_reg = 1'b0, temp_m_axi_awvalid_next; + +// datapath control +reg store_axi_aw_input_to_output; +reg store_axi_aw_input_to_temp; +reg store_axi_aw_temp_to_output; + +assign s_axi_awready = s_axi_awready_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_awready_early = m_axi_awready | (~temp_m_axi_awvalid_reg & (~m_axi_awvalid_reg | ~s_axi_awvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_awvalid_next = m_axi_awvalid_reg; + temp_m_axi_awvalid_next = temp_m_axi_awvalid_reg; + + store_axi_aw_input_to_output = 1'b0; + store_axi_aw_input_to_temp = 1'b0; + store_axi_aw_temp_to_output = 1'b0; + + if (s_axi_awready_reg) begin + // input is ready + if (m_axi_awready | ~m_axi_awvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_temp = 1'b1; + end + end else if (m_axi_awready) begin + // input is not ready, but output is ready + m_axi_awvalid_next = temp_m_axi_awvalid_reg; + temp_m_axi_awvalid_next = 1'b0; + store_axi_aw_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_awready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + temp_m_axi_awvalid_reg <= 1'b0; + end else begin + s_axi_awready_reg <= s_axi_awready_early; + m_axi_awvalid_reg <= m_axi_awvalid_next; + temp_m_axi_awvalid_reg <= temp_m_axi_awvalid_next; + end + + // datapath + if (store_axi_aw_input_to_output) begin + m_axi_awid_reg <= s_axi_awid; + m_axi_awaddr_reg <= s_axi_awaddr; + m_axi_awlen_reg <= s_axi_awlen; + m_axi_awsize_reg <= s_axi_awsize; + m_axi_awburst_reg <= s_axi_awburst; + m_axi_awlock_reg <= s_axi_awlock; + m_axi_awcache_reg <= s_axi_awcache; + m_axi_awprot_reg <= s_axi_awprot; + m_axi_awqos_reg <= s_axi_awqos; + m_axi_awregion_reg <= s_axi_awregion; + m_axi_awuser_reg <= s_axi_awuser; + end else if (store_axi_aw_temp_to_output) begin + m_axi_awid_reg <= temp_m_axi_awid_reg; + m_axi_awaddr_reg <= temp_m_axi_awaddr_reg; + m_axi_awlen_reg <= temp_m_axi_awlen_reg; + m_axi_awsize_reg <= temp_m_axi_awsize_reg; + m_axi_awburst_reg <= temp_m_axi_awburst_reg; + m_axi_awlock_reg <= temp_m_axi_awlock_reg; + m_axi_awcache_reg <= temp_m_axi_awcache_reg; + m_axi_awprot_reg <= temp_m_axi_awprot_reg; + m_axi_awqos_reg <= temp_m_axi_awqos_reg; + m_axi_awregion_reg <= temp_m_axi_awregion_reg; + m_axi_awuser_reg <= temp_m_axi_awuser_reg; + end + + if (store_axi_aw_input_to_temp) begin + temp_m_axi_awid_reg <= s_axi_awid; + temp_m_axi_awaddr_reg <= s_axi_awaddr; + temp_m_axi_awlen_reg <= s_axi_awlen; + temp_m_axi_awsize_reg <= s_axi_awsize; + temp_m_axi_awburst_reg <= s_axi_awburst; + temp_m_axi_awlock_reg <= s_axi_awlock; + temp_m_axi_awcache_reg <= s_axi_awcache; + temp_m_axi_awprot_reg <= s_axi_awprot; + temp_m_axi_awqos_reg <= s_axi_awqos; + temp_m_axi_awregion_reg <= s_axi_awregion; + temp_m_axi_awuser_reg <= s_axi_awuser; + end +end + +end else if (AW_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_awready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_awlen_reg = 8'd0; +reg [2:0] m_axi_awsize_reg = 3'd0; +reg [1:0] m_axi_awburst_reg = 2'd0; +reg m_axi_awlock_reg = 1'b0; +reg [3:0] m_axi_awcache_reg = 4'd0; +reg [2:0] m_axi_awprot_reg = 3'd0; +reg [3:0] m_axi_awqos_reg = 4'd0; +reg [3:0] m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +// datapath control +reg store_axi_aw_input_to_output; + +assign s_axi_awready = s_axi_awready_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_awready_eawly = !m_axi_awvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_awvalid_next = m_axi_awvalid_reg; + + store_axi_aw_input_to_output = 1'b0; + + if (s_axi_awready_reg) begin + m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_output = 1'b1; + end else if (m_axi_awready) begin + m_axi_awvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_awready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + end else begin + s_axi_awready_reg <= s_axi_awready_eawly; + m_axi_awvalid_reg <= m_axi_awvalid_next; + end + + // datapath + if (store_axi_aw_input_to_output) begin + m_axi_awid_reg <= s_axi_awid; + m_axi_awaddr_reg <= s_axi_awaddr; + m_axi_awlen_reg <= s_axi_awlen; + m_axi_awsize_reg <= s_axi_awsize; + m_axi_awburst_reg <= s_axi_awburst; + m_axi_awlock_reg <= s_axi_awlock; + m_axi_awcache_reg <= s_axi_awcache; + m_axi_awprot_reg <= s_axi_awprot; + m_axi_awqos_reg <= s_axi_awqos; + m_axi_awregion_reg <= s_axi_awregion; + m_axi_awuser_reg <= s_axi_awuser; + end +end + +end else begin + + // bypass AW channel + assign m_axi_awid = s_axi_awid; + assign m_axi_awaddr = s_axi_awaddr; + assign m_axi_awlen = s_axi_awlen; + assign m_axi_awsize = s_axi_awsize; + assign m_axi_awburst = s_axi_awburst; + assign m_axi_awlock = s_axi_awlock; + assign m_axi_awcache = s_axi_awcache; + assign m_axi_awprot = s_axi_awprot; + assign m_axi_awqos = s_axi_awqos; + assign m_axi_awregion = s_axi_awregion; + assign m_axi_awuser = AWUSER_ENABLE ? s_axi_awuser : {AWUSER_WIDTH{1'b0}}; + assign m_axi_awvalid = s_axi_awvalid; + assign s_axi_awready = m_axi_awready; + +end + +// W channel + +if (W_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_input_to_output; +reg store_axi_w_input_to_temp; +reg store_axi_w_temp_to_output; + +assign s_axi_wready = s_axi_wready_reg; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_wready_early = m_axi_wready | (~temp_m_axi_wvalid_reg & (~m_axi_wvalid_reg | ~s_axi_wvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_input_to_output = 1'b0; + store_axi_w_input_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (s_axi_wready_reg) begin + // input is ready + if (m_axi_wready | ~m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_wready_reg <= 1'b0; + m_axi_wvalid_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + s_axi_wready_reg <= s_axi_wready_early; + m_axi_wvalid_reg <= m_axi_wvalid_next; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_input_to_output) begin + m_axi_wdata_reg <= s_axi_wdata; + m_axi_wstrb_reg <= s_axi_wstrb; + m_axi_wlast_reg <= s_axi_wlast; + m_axi_wuser_reg <= s_axi_wuser; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_input_to_temp) begin + temp_m_axi_wdata_reg <= s_axi_wdata; + temp_m_axi_wstrb_reg <= s_axi_wstrb; + temp_m_axi_wlast_reg <= s_axi_wlast; + temp_m_axi_wuser_reg <= s_axi_wuser; + end +end + +end else if (W_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +// datapath control +reg store_axi_w_input_to_output; + +assign s_axi_wready = s_axi_wready_reg; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_wready_ewly = !m_axi_wvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + + store_axi_w_input_to_output = 1'b0; + + if (s_axi_wready_reg) begin + m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_output = 1'b1; + end else if (m_axi_wready) begin + m_axi_wvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_wready_reg <= 1'b0; + m_axi_wvalid_reg <= 1'b0; + end else begin + s_axi_wready_reg <= s_axi_wready_ewly; + m_axi_wvalid_reg <= m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_input_to_output) begin + m_axi_wdata_reg <= s_axi_wdata; + m_axi_wstrb_reg <= s_axi_wstrb; + m_axi_wlast_reg <= s_axi_wlast; + m_axi_wuser_reg <= s_axi_wuser; + end +end + +end else begin + + // bypass W channel + assign m_axi_wdata = s_axi_wdata; + assign m_axi_wstrb = s_axi_wstrb; + assign m_axi_wlast = s_axi_wlast; + assign m_axi_wuser = WUSER_ENABLE ? s_axi_wuser : {WUSER_WIDTH{1'b0}}; + assign m_axi_wvalid = s_axi_wvalid; + assign s_axi_wready = m_axi_wready; + +end + +// B channel + +if (B_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axi_bready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] temp_s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg temp_s_axi_bvalid_reg = 1'b0, temp_s_axi_bvalid_next; + +// datapath control +reg store_axi_b_input_to_output; +reg store_axi_b_input_to_temp; +reg store_axi_b_temp_to_output; + +assign m_axi_bready = m_axi_bready_reg; + +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axi_bready_early = s_axi_bready | (~temp_s_axi_bvalid_reg & (~s_axi_bvalid_reg | ~m_axi_bvalid)); + +always @* begin + // transfer sink ready state to source + s_axi_bvalid_next = s_axi_bvalid_reg; + temp_s_axi_bvalid_next = temp_s_axi_bvalid_reg; + + store_axi_b_input_to_output = 1'b0; + store_axi_b_input_to_temp = 1'b0; + store_axi_b_temp_to_output = 1'b0; + + if (m_axi_bready_reg) begin + // input is ready + if (s_axi_bready | ~s_axi_bvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_temp = 1'b1; + end + end else if (s_axi_bready) begin + // input is not ready, but output is ready + s_axi_bvalid_next = temp_s_axi_bvalid_reg; + temp_s_axi_bvalid_next = 1'b0; + store_axi_b_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_bready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + temp_s_axi_bvalid_reg <= 1'b0; + end else begin + m_axi_bready_reg <= m_axi_bready_early; + s_axi_bvalid_reg <= s_axi_bvalid_next; + temp_s_axi_bvalid_reg <= temp_s_axi_bvalid_next; + end + + // datapath + if (store_axi_b_input_to_output) begin + s_axi_bid_reg <= m_axi_bid; + s_axi_bresp_reg <= m_axi_bresp; + s_axi_buser_reg <= m_axi_buser; + end else if (store_axi_b_temp_to_output) begin + s_axi_bid_reg <= temp_s_axi_bid_reg; + s_axi_bresp_reg <= temp_s_axi_bresp_reg; + s_axi_buser_reg <= temp_s_axi_buser_reg; + end + + if (store_axi_b_input_to_temp) begin + temp_s_axi_bid_reg <= m_axi_bid; + temp_s_axi_bresp_reg <= m_axi_bresp; + temp_s_axi_buser_reg <= m_axi_buser; + end +end + +end else if (B_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axi_bready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +// datapath control +reg store_axi_b_input_to_output; + +assign m_axi_bready = m_axi_bready_reg; + +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axi_bready_early = !s_axi_bvalid_next; + +always @* begin + // transfer sink ready state to source + s_axi_bvalid_next = s_axi_bvalid_reg; + + store_axi_b_input_to_output = 1'b0; + + if (m_axi_bready_reg) begin + s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_output = 1'b1; + end else if (s_axi_bready) begin + s_axi_bvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_bready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + end else begin + m_axi_bready_reg <= m_axi_bready_early; + s_axi_bvalid_reg <= s_axi_bvalid_next; + end + + // datapath + if (store_axi_b_input_to_output) begin + s_axi_bid_reg <= m_axi_bid; + s_axi_bresp_reg <= m_axi_bresp; + s_axi_buser_reg <= m_axi_buser; + end +end + +end else begin + + // bypass B channel + assign s_axi_bid = m_axi_bid; + assign s_axi_bresp = m_axi_bresp; + assign s_axi_buser = BUSER_ENABLE ? m_axi_buser : {BUSER_WIDTH{1'b0}}; + assign s_axi_bvalid = m_axi_bvalid; + assign m_axi_bready = s_axi_bready; + +end + +endgenerate + +endmodule + +`resetall diff --git a/testbench/axi4_mux/priority_encoder.v b/testbench/axi4_mux/priority_encoder.v new file mode 100644 index 00000000000..cf82512ba84 --- /dev/null +++ b/testbench/axi4_mux/priority_encoder.v @@ -0,0 +1,92 @@ +/* + +Copyright (c) 2014-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority selection + parameter LSB_HIGH_PRIORITY = 0 +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +parameter LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1; +parameter W = 2**LEVELS; + +// pad input to even power of two +wire [W-1:0] input_padded = {{W-WIDTH{1'b0}}, input_unencoded}; + +wire [W/2-1:0] stage_valid[LEVELS-1:0]; +wire [W/2-1:0] stage_enc[LEVELS-1:0]; + +generate + genvar l, n; + + // process input bits; generate valid bit and encoded bit for each pair + for (n = 0; n < W/2; n = n + 1) begin : loop_in + assign stage_valid[0][n] = |input_padded[n*2+1:n*2]; + if (LSB_HIGH_PRIORITY) begin + // bit 0 is highest priority + assign stage_enc[0][n] = !input_padded[n*2+0]; + end else begin + // bit 0 is lowest priority + assign stage_enc[0][n] = input_padded[n*2+1]; + end + end + + // compress down to single valid bit and encoded bus + for (l = 1; l < LEVELS; l = l + 1) begin : loop_levels + for (n = 0; n < W/(2*2**l); n = n + 1) begin : loop_compress + assign stage_valid[l][n] = |stage_valid[l-1][n*2+1:n*2]; + if (LSB_HIGH_PRIORITY) begin + // bit 0 is highest priority + assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+0] ? {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]} : {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]}; + end else begin + // bit 0 is lowest priority + assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+1] ? {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]} : {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]}; + end + end + end +endgenerate + +assign output_valid = stage_valid[LEVELS-1]; +assign output_encoded = stage_enc[LEVELS-1]; +assign output_unencoded = 1 << output_encoded; + +endmodule + +`resetall diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index e09d071a6ca..4691d28ebe7 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -244,6 +244,12 @@ module tb_top wire [63:0] lsu_axi_rdata; wire [1:0] lsu_axi_rresp; wire lsu_axi_rlast; + wire lsu_axi_awuser; + wire lsu_axi_wlast; + wire lsu_axi_wuser; + wire lsu_axi_buser; + wire lsu_axi_aruser; + wire lsu_axi_ruser; //-------------------------- IFU AXI signals-------------------------- // AXI Write Channels @@ -338,6 +344,12 @@ module tb_top wire [63:0] sb_axi_rdata; wire [1:0] sb_axi_rresp; wire sb_axi_rlast; + wire sb_axi_awuser; + wire sb_axi_wlast; + wire sb_axi_wuser; + wire sb_axi_buser; + wire sb_axi_aruser; + wire sb_axi_ruser; //-------------------------- DMA AXI signals-------------------------- // AXI Write Channels @@ -400,6 +412,213 @@ module tb_top wire [`RV_LSU_BUS_TAG-1:0] lmem_axi_bid; wire lmem_axi_bready; + wire mux_axi_awvalid; + wire mux_axi_awready; + wire [`RV_SB_BUS_TAG-1:0] mux_axi_awid; + wire [31:0] mux_axi_awaddr; + wire [3:0] mux_axi_awregion; + wire [7:0] mux_axi_awlen; + wire [2:0] mux_axi_awsize; + wire [1:0] mux_axi_awburst; + wire mux_axi_awlock; + wire [3:0] mux_axi_awcache; + wire [2:0] mux_axi_awprot; + wire [3:0] mux_axi_awqos; + + wire mux_axi_wvalid; + wire mux_axi_wready; + wire [63:0] mux_axi_wdata; + wire [7:0] mux_axi_wstrb; + wire mux_axi_wlast; + + wire mux_axi_bvalid; + wire mux_axi_bready; + wire [1:0] mux_axi_bresp; + wire [`RV_SB_BUS_TAG-1:0] mux_axi_bid; + + // AXI Read Channels + wire mux_axi_arvalid; + wire mux_axi_arready; + wire [`RV_SB_BUS_TAG-1:0] mux_axi_arid; + wire [31:0] mux_axi_araddr; + wire [3:0] mux_axi_arregion; + wire [7:0] mux_axi_arlen; + wire [2:0] mux_axi_arsize; + wire [1:0] mux_axi_arburst; + wire mux_axi_arlock; + wire [3:0] mux_axi_arcache; + wire [2:0] mux_axi_arprot; + wire [3:0] mux_axi_arqos; + + wire mux_axi_rvalid; + wire mux_axi_rready; + wire [`RV_SB_BUS_TAG-1:0] mux_axi_rid; + wire [63:0] mux_axi_rdata; + wire [1:0] mux_axi_rresp; + wire mux_axi_rlast; + wire mux_axi_awuser; + wire mux_axi_wlast; + wire mux_axi_wuser; + wire mux_axi_buser; + wire mux_axi_aruser; + wire mux_axi_ruser; + wire mux_axi_awregion; + wire mux_axi_arregion; + + axi_crossbar_wrap_2x1 #( + .ADDR_WIDTH (32), + .DATA_WIDTH (64) + ) u_axi_crossbar ( + .clk(core_clk), + .rst(!rst_l), + + // LSU + .s00_axi_arvalid(lsu_axi_arvalid), + .s00_axi_arready(lsu_axi_arready), + .s00_axi_araddr(lsu_axi_araddr), + .s00_axi_arid(lsu_axi_arid), + .s00_axi_arlen(lsu_axi_arlen), + .s00_axi_arburst(lsu_axi_arburst), + .s00_axi_arsize(lsu_axi_arsize), + + .s00_axi_rvalid(lsu_axi_rvalid), + .s00_axi_rready(lsu_axi_rready), + .s00_axi_rdata(lsu_axi_rdata), + .s00_axi_rresp(lsu_axi_rresp), + .s00_axi_rid(lsu_axi_rid), + .s00_axi_rlast(lsu_axi_rlast), + + .s00_axi_awvalid(lsu_axi_awvalid), + .s00_axi_awready(lsu_axi_awready), + .s00_axi_awaddr(lsu_axi_awaddr), + .s00_axi_awid(lsu_axi_awid), + .s00_axi_awlen(lsu_axi_awlen), + .s00_axi_awburst(lsu_axi_awburst), + .s00_axi_awlock(lsu_axi_awlock), + .s00_axi_awcache(lsu_axi_awcache), + .s00_axi_awprot(lsu_axi_awprot), + .s00_axi_awqos(lsu_axi_awqos), + .s00_axi_awuser(lsu_axi_awuser), + .s00_axi_wlast(lsu_axi_wlast), + .s00_axi_wuser(lsu_axi_wuser), + .s00_axi_buser(lsu_axi_buser), + .s00_axi_arlock(lsu_axi_arlock), + .s00_axi_arcache(lsu_axi_arcache), + .s00_axi_arprot(lsu_axi_arprot), + .s00_axi_arqos(lsu_axi_arqos), + .s00_axi_aruser(lsu_axi_aruser), + .s00_axi_ruser(lsu_axi_ruser), + .s00_axi_awsize(lsu_axi_awsize), + + .s00_axi_wdata(lsu_axi_wdata), + .s00_axi_wstrb(lsu_axi_wstrb), + .s00_axi_wvalid(lsu_axi_wvalid), + .s00_axi_wready(lsu_axi_wready), + + .s00_axi_bvalid(lsu_axi_bvalid), + .s00_axi_bready(lsu_axi_bready), + .s00_axi_bresp(lsu_axi_bresp), + .s00_axi_bid(lsu_axi_bid), + + // SB + .s01_axi_arvalid(sb_axi_arvalid), + .s01_axi_arready(sb_axi_arready), + .s01_axi_araddr(sb_axi_araddr), + .s01_axi_arid(sb_axi_arid), + .s01_axi_arlen(sb_axi_arlen), + .s01_axi_arburst(sb_axi_arburst), + .s01_axi_arsize(sb_axi_arsize), + + .s01_axi_rvalid(sb_axi_rvalid), + .s01_axi_rready(sb_axi_rready), + .s01_axi_rdata(sb_axi_rdata), + .s01_axi_rresp(sb_axi_rresp), + .s01_axi_rid(sb_axi_rid), + .s01_axi_rlast(sb_axi_rlast), + + .s01_axi_awvalid(sb_axi_awvalid), + .s01_axi_awready(sb_axi_awready), + .s01_axi_awaddr(sb_axi_awaddr), + .s01_axi_awid(sb_axi_awid), + .s01_axi_awlen(sb_axi_awlen), + .s01_axi_awburst(sb_axi_awburst), + .s01_axi_awlock(sb_axi_awlock), + .s01_axi_awcache(sb_axi_awcache), + .s01_axi_awprot(sb_axi_awprot), + .s01_axi_awqos(sb_axi_awqos), + .s01_axi_awuser(sb_axi_awuser), + .s01_axi_wlast(sb_axi_wlast), + .s01_axi_wuser(sb_axi_wuser), + .s01_axi_buser(sb_axi_buser), + .s01_axi_arlock(sb_axi_arlock), + .s01_axi_arcache(sb_axi_arcache), + .s01_axi_arprot(sb_axi_arprot), + .s01_axi_arqos(sb_axi_arqos), + .s01_axi_aruser(sb_axi_aruser), + .s01_axi_ruser(sb_axi_ruser), + .s01_axi_awsize(sb_axi_awsize), + + .s01_axi_wdata(sb_axi_wdata), + .s01_axi_wstrb(sb_axi_wstrb), + .s01_axi_wvalid(sb_axi_wvalid), + .s01_axi_wready(sb_axi_wready), + + .s01_axi_bvalid(sb_axi_bvalid), + .s01_axi_bready(sb_axi_bready), + .s01_axi_bresp(sb_axi_bresp), + .s01_axi_bid(sb_axi_bid), + + // Output + .m00_axi_arvalid(mux_axi_arvalid), + .m00_axi_arready(mux_axi_arready), + .m00_axi_araddr(mux_axi_araddr), + .m00_axi_arid(mux_axi_arid), + .m00_axi_arlen(mux_axi_arlen), + .m00_axi_arburst(mux_axi_arburst), + .m00_axi_arsize(mux_axi_arsize), + + .m00_axi_rvalid(mux_axi_rvalid), + .m00_axi_rready(mux_axi_rready), + .m00_axi_rdata(mux_axi_rdata), + .m00_axi_rresp(mux_axi_rresp), + .m00_axi_rid(mux_axi_rid), + .m00_axi_rlast(mux_axi_rlast), + + .m00_axi_awvalid(mux_axi_awvalid), + .m00_axi_awready(mux_axi_awready), + .m00_axi_awaddr(mux_axi_awaddr), + .m00_axi_awid(mux_axi_awid), + .m00_axi_awlen(mux_axi_awlen), + .m00_axi_awburst(mux_axi_awburst), + .m00_axi_awlock(mux_axi_awlock), + .m00_axi_awcache(mux_axi_awcache), + .m00_axi_awprot(mux_axi_awprot), + .m00_axi_awqos(mux_axi_awqos), + .m00_axi_awuser(mux_axi_awuser), + .m00_axi_wlast(mux_axi_wlast), + .m00_axi_wuser(mux_axi_wuser), + .m00_axi_buser(mux_axi_buser), + .m00_axi_arlock(mux_axi_arlock), + .m00_axi_arcache(mux_axi_arcache), + .m00_axi_arprot(mux_axi_arprot), + .m00_axi_arqos(mux_axi_arqos), + .m00_axi_aruser(mux_axi_aruser), + .m00_axi_ruser(mux_axi_ruser), + .m00_axi_awsize(mux_axi_awsize), + + .m00_axi_wdata(mux_axi_wdata), + .m00_axi_wstrb(mux_axi_wstrb), + .m00_axi_wvalid(mux_axi_wvalid), + .m00_axi_wready(mux_axi_wready), + + .m00_axi_bvalid(mux_axi_bvalid), + .m00_axi_bready(mux_axi_bready), + .m00_axi_bresp(mux_axi_bresp), + .m00_axi_bid(mux_axi_bid), + .m00_axi_awregion(mux_axi_awregion), + .m00_axi_arregion(mux_axi_arregion) + ); + `endif string abi_reg[32]; // ABI register names el2_mem_if el2_mem_export (); @@ -1045,11 +1264,11 @@ axi_slv lmem( .rst_l(rst_l), .arvalid(lmem_axi_arvalid), .arready(lmem_axi_arready), - .araddr(lsu_axi_araddr), - .arid(lsu_axi_arid), - .arlen(lsu_axi_arlen), - .arburst(lsu_axi_arburst), - .arsize(lsu_axi_arsize), + .araddr(mux_axi_araddr), + .arid(mux_axi_arid), + .arlen(mux_axi_arlen), + .arburst(mux_axi_arburst), + .arsize(mux_axi_arsize), .rvalid(lmem_axi_rvalid), .rready(lmem_axi_rready), @@ -1060,14 +1279,14 @@ axi_slv lmem( .awvalid(lmem_axi_awvalid), .awready(lmem_axi_awready), - .awaddr(lsu_axi_awaddr), - .awid(lsu_axi_awid), - .awlen(lsu_axi_awlen), - .awburst(lsu_axi_awburst), - .awsize(lsu_axi_awsize), - - .wdata(lsu_axi_wdata), - .wstrb(lsu_axi_wstrb), + .awaddr(mux_axi_awaddr), + .awid(mux_axi_awid), + .awlen(mux_axi_awlen), + .awburst(mux_axi_awburst), + .awsize(mux_axi_awsize), + + .wdata(mux_axi_wdata), + .wstrb(mux_axi_wstrb), .wvalid(lmem_axi_wvalid), .wready(lmem_axi_wready), @@ -1081,30 +1300,30 @@ axi_lsu_dma_bridge # (`RV_LSU_BUS_TAG,`RV_LSU_BUS_TAG ) bridge( .clk(core_clk), .reset_l(rst_l), - .m_arvalid(lsu_axi_arvalid), - .m_arid(lsu_axi_arid), - .m_araddr(lsu_axi_araddr), - .m_arready(lsu_axi_arready), - - .m_rvalid(lsu_axi_rvalid), - .m_rready(lsu_axi_rready), - .m_rdata(lsu_axi_rdata), - .m_rid(lsu_axi_rid), - .m_rresp(lsu_axi_rresp), - .m_rlast(lsu_axi_rlast), - - .m_awvalid(lsu_axi_awvalid), - .m_awid(lsu_axi_awid), - .m_awaddr(lsu_axi_awaddr), - .m_awready(lsu_axi_awready), - - .m_wvalid(lsu_axi_wvalid), - .m_wready(lsu_axi_wready), - - .m_bresp(lsu_axi_bresp), - .m_bvalid(lsu_axi_bvalid), - .m_bid(lsu_axi_bid), - .m_bready(lsu_axi_bready), + .m_arvalid(mux_axi_arvalid), + .m_arid(mux_axi_arid), + .m_araddr(mux_axi_araddr), + .m_arready(mux_axi_arready), + + .m_rvalid(mux_axi_rvalid), + .m_rready(mux_axi_rready), + .m_rdata(mux_axi_rdata), + .m_rid(mux_axi_rid), + .m_rresp(mux_axi_rresp), + .m_rlast(mux_axi_rlast), + + .m_awvalid(mux_axi_awvalid), + .m_awid(mux_axi_awid), + .m_awaddr(mux_axi_awaddr), + .m_awready(mux_axi_awready), + + .m_wvalid(mux_axi_wvalid), + .m_wready(mux_axi_wready), + + .m_bresp(mux_axi_bresp), + .m_bvalid(mux_axi_bvalid), + .m_bid(mux_axi_bid), + .m_bready(mux_axi_bready), .s0_arvalid(lmem_axi_arvalid), .s0_arready(lmem_axi_arready), diff --git a/tools/Makefile b/tools/Makefile index 44d9d1f6ef7..2b5bdefb2dd 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -69,6 +69,8 @@ ifeq ("$(.SHELLSTATUS)", "0") endif endif +VERILATOR_SKIP_WARNINGS = $(VERILATOR_NOIMPLICIT) -Wno-TIMESCALEMOD + # Define test name TEST = hello_world TEST_DIR = ${TBDIR}/asm @@ -141,7 +143,7 @@ TBFILES = $(TBDIR)/tb_top_pkg.sv \ defines = $(BUILD_DIR)/common_defines.vh defines += ${RV_ROOT}/design/include/el2_def.sv defines += $(BUILD_DIR)/el2_pdef.vh -includes = -I${BUILD_DIR} +includes = -I${BUILD_DIR} -I$(TBDIR)/axi4_mux # Verilator supports only C++14 and newer CFLAGS += -std=c++14 @@ -170,7 +172,7 @@ ${BUILD_DIR}/defines.h: verilator-build: ${TBFILES} ${BUILD_DIR}/defines.h $(TB_VERILATOR_SRCS) $(VERILATOR) --cc -CFLAGS "${CFLAGS}" $(defines) \ $(includes) -I${RV_ROOT}/testbench -f ${RV_ROOT}/testbench/flist \ - -Wno-WIDTH -Wno-UNOPTFLAT $(VERILATOR_NOIMPLICIT) \ + -Wno-WIDTH -Wno-UNOPTFLAT $(VERILATOR_SKIP_WARNINGS) \ ${TBFILES} --top-module tb_top \ -exe $(TB_VERILATOR_SRCS) --autoflush $(VERILATOR_DEBUG) $(VERILATOR_COVERAGE) cp ${RV_ROOT}/testbench/test_tb_top.cpp obj_dir/ From 1cabd8efa1709720e106b5066d55af1778e6c46f Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Mon, 1 Jul 2024 09:42:15 +0200 Subject: [PATCH 04/14] Skip Verilator warnings Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- tools/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/Makefile b/tools/Makefile index 2b5bdefb2dd..969456af6e4 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -69,7 +69,8 @@ ifeq ("$(.SHELLSTATUS)", "0") endif endif -VERILATOR_SKIP_WARNINGS = $(VERILATOR_NOIMPLICIT) -Wno-TIMESCALEMOD +VERILATOR_SKIP_WARNINGS = $(VERILATOR_NOIMPLICIT) -Wno-TIMESCALEMOD -Wno-ASCRANGE \ + -Wno-CASEINCOMPLETE -Wno-INITIALDLY -Wno-WIDTH -Wno-UNOPTFLAT # Define test name TEST = hello_world @@ -172,8 +173,7 @@ ${BUILD_DIR}/defines.h: verilator-build: ${TBFILES} ${BUILD_DIR}/defines.h $(TB_VERILATOR_SRCS) $(VERILATOR) --cc -CFLAGS "${CFLAGS}" $(defines) \ $(includes) -I${RV_ROOT}/testbench -f ${RV_ROOT}/testbench/flist \ - -Wno-WIDTH -Wno-UNOPTFLAT $(VERILATOR_SKIP_WARNINGS) \ - ${TBFILES} --top-module tb_top \ + $(VERILATOR_SKIP_WARNINGS) ${TBFILES} --top-module tb_top \ -exe $(TB_VERILATOR_SRCS) --autoflush $(VERILATOR_DEBUG) $(VERILATOR_COVERAGE) cp ${RV_ROOT}/testbench/test_tb_top.cpp obj_dir/ $(MAKE) -e -C obj_dir/ -f Vtb_top.mk $(VERILATOR_MAKE_FLAGS) From de131c3558d1a54bedaffff7cc38b18bc7cb9cf3 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 3 Jul 2024 16:14:01 +0200 Subject: [PATCH 05/14] Fix axi_slv signals Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- testbench/ahb_sif.sv | 76 ++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/testbench/ahb_sif.sv b/testbench/ahb_sif.sv index f41acc6b3bb..d4ef94e7657 100644 --- a/testbench/ahb_sif.sv +++ b/testbench/ahb_sif.sv @@ -179,28 +179,20 @@ module axi_slv #( output reg [TAGW-1:0] bid ); - parameter MAILBOX_ADDR = 32'hD0580000; - parameter MEM_SIZE_DW = 8192; - bit [7:0] mem[bit [31:0]]; - bit [63:0] memdata; + bit [31:0] write_address; - always @(posedge aclk or negedge rst_l) begin - if (!rst_l) begin - rvalid <= 0; - bvalid <= 0; - end else begin - bid <= awid; - rid <= arid; - rvalid <= arvalid; - bvalid <= awvalid; - rdata <= memdata; - end + initial begin + wready = 1; + awready = 1; + arready = 1'b1; + rlast = 1'b0; + rvalid = 0; end - always @(negedge aclk) begin - if (arvalid) - memdata <= { + always @(posedge aclk) begin + if (arvalid && arready) begin + rdata <= { mem[araddr+7], mem[araddr+6], mem[araddr+5], @@ -210,26 +202,40 @@ module axi_slv #( mem[araddr+1], mem[araddr] }; + arready <= 0; + rvalid <= 1; + rid <= arid; + rlast <= 1; + rresp <= 0; + end else if (rready) begin + rvalid <= 0; + arready <= 1; + rlast <= 0; + end + if (awvalid) begin - if (wstrb[7]) mem[awaddr+7] = wdata[63:56]; - if (wstrb[6]) mem[awaddr+6] = wdata[55:48]; - if (wstrb[5]) mem[awaddr+5] = wdata[47:40]; - if (wstrb[4]) mem[awaddr+4] = wdata[39:32]; - if (wstrb[3]) mem[awaddr+3] = wdata[31:24]; - if (wstrb[2]) mem[awaddr+2] = wdata[23:16]; - if (wstrb[1]) mem[awaddr+1] = wdata[15:08]; - if (wstrb[0]) mem[awaddr+0] = wdata[07:00]; + write_address = awaddr; + awready <= 0; end + if (wvalid) begin + bid <= awid; + bvalid <= 1; + wready <= 0; + bresp <= 0; + if (wstrb[7]) mem[write_address+7] = wdata[63:56]; + if (wstrb[6]) mem[write_address+6] = wdata[55:48]; + if (wstrb[5]) mem[write_address+5] = wdata[47:40]; + if (wstrb[4]) mem[write_address+4] = wdata[39:32]; + if (wstrb[3]) mem[write_address+3] = wdata[31:24]; + if (wstrb[2]) mem[write_address+2] = wdata[23:16]; + if (wstrb[1]) mem[write_address+1] = wdata[15:08]; + if (wstrb[0]) mem[write_address+0] = wdata[07:00]; + end if (bready && bvalid) begin + bvalid <= 0; + awready <= 1; + wready <= 1; + end end - - - assign arready = 1'b1; - assign awready = 1'b1; - assign wready = 1'b1; - assign rresp = 2'b0; - assign bresp = 2'b0; - assign rlast = 1'b1; - endmodule `endif From e6936e939172874b3f069d1ca05c2f4e41bba9d4 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 10 Jul 2024 16:41:39 +0200 Subject: [PATCH 06/14] Change width of mux output Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- testbench/tb_top.sv | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index 4691d28ebe7..d96dae08c8e 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -200,6 +200,7 @@ module tb_top `ifdef RV_BUILD_AXI4 //-------------------------- LSU AXI signals-------------------------- // AXI Write Channels + parameter int RV_MUX_BUS_TAG = (`RV_LSU_BUS_TAG > `RV_SB_BUS_TAG ? `RV_LSU_BUS_TAG : `RV_SB_BUS_TAG) + 1; wire lsu_axi_awvalid; wire lsu_axi_awready; wire [`RV_LSU_BUS_TAG-1:0] lsu_axi_awid; @@ -395,7 +396,7 @@ module tb_top wire lmem_axi_arready; wire lmem_axi_rvalid; - wire [`RV_LSU_BUS_TAG-1:0] lmem_axi_rid; + wire [RV_MUX_BUS_TAG-1:0] lmem_axi_rid; wire [1:0] lmem_axi_rresp; wire [63:0] lmem_axi_rdata; wire lmem_axi_rlast; @@ -409,12 +410,12 @@ module tb_top wire [1:0] lmem_axi_bresp; wire lmem_axi_bvalid; - wire [`RV_LSU_BUS_TAG-1:0] lmem_axi_bid; + wire [RV_MUX_BUS_TAG-1:0] lmem_axi_bid; wire lmem_axi_bready; wire mux_axi_awvalid; wire mux_axi_awready; - wire [`RV_SB_BUS_TAG-1:0] mux_axi_awid; + wire [RV_MUX_BUS_TAG-1:0] mux_axi_awid; wire [31:0] mux_axi_awaddr; wire [3:0] mux_axi_awregion; wire [7:0] mux_axi_awlen; @@ -434,12 +435,12 @@ module tb_top wire mux_axi_bvalid; wire mux_axi_bready; wire [1:0] mux_axi_bresp; - wire [`RV_SB_BUS_TAG-1:0] mux_axi_bid; + wire [RV_MUX_BUS_TAG-1:0] mux_axi_bid; // AXI Read Channels wire mux_axi_arvalid; wire mux_axi_arready; - wire [`RV_SB_BUS_TAG-1:0] mux_axi_arid; + wire [RV_MUX_BUS_TAG-1:0] mux_axi_arid; wire [31:0] mux_axi_araddr; wire [3:0] mux_axi_arregion; wire [7:0] mux_axi_arlen; @@ -452,7 +453,7 @@ module tb_top wire mux_axi_rvalid; wire mux_axi_rready; - wire [`RV_SB_BUS_TAG-1:0] mux_axi_rid; + wire [RV_MUX_BUS_TAG-1:0] mux_axi_rid; wire [63:0] mux_axi_rdata; wire [1:0] mux_axi_rresp; wire mux_axi_rlast; @@ -467,7 +468,9 @@ module tb_top axi_crossbar_wrap_2x1 #( .ADDR_WIDTH (32), - .DATA_WIDTH (64) + .DATA_WIDTH (64), + .S_ID_WIDTH(RV_MUX_BUS_TAG - 1), + .M00_ADDR_WIDTH(32) ) u_axi_crossbar ( .clk(core_clk), .rst(!rst_l), @@ -731,7 +734,6 @@ module tb_top end end - // trace monitor always @(posedge core_clk) begin wb_valid <= `DEC.dec_i0_wen_r; @@ -1256,7 +1258,7 @@ axi_slv #(.TAGW(`RV_IFU_BUS_TAG)) imem( .bid() ); -defparam lmem.TAGW =`RV_LSU_BUS_TAG; +defparam lmem.TAGW = RV_MUX_BUS_TAG; //axi_slv #(.TAGW(`RV_LSU_BUS_TAG)) lmem( axi_slv lmem( @@ -1296,7 +1298,7 @@ axi_slv lmem( .bid(lmem_axi_bid) ); -axi_lsu_dma_bridge # (`RV_LSU_BUS_TAG,`RV_LSU_BUS_TAG ) bridge( +axi_lsu_dma_bridge # (RV_MUX_BUS_TAG, RV_MUX_BUS_TAG) bridge( .clk(core_clk), .reset_l(rst_l), From f191c87612bda3a41b1e6ad52b5ee29a04d68f1e Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 11 Jul 2024 15:46:46 +0200 Subject: [PATCH 07/14] Add licence information Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/scripts/openocd_test.sh | 1 + .github/scripts/utils.sh | 1 + testbench/ahb_lite_2to1_mux.sv | 1 + testbench/ahb_sif.sv | 3 ++- testbench/asm/infinite_loop.s | 1 + testbench/axi4_mux/arbiter.v | 1 + testbench/axi4_mux/axi_crossbar.v | 1 + testbench/axi4_mux/axi_crossbar_addr.v | 1 + testbench/axi4_mux/axi_crossbar_rd.v | 1 + testbench/axi4_mux/axi_crossbar_wr.v | 1 + testbench/axi4_mux/axi_crossbar_wrap_2x1.v | 1 + testbench/axi4_mux/axi_register_rd.v | 1 + testbench/axi4_mux/axi_register_wr.v | 1 + testbench/axi4_mux/priority_encoder.v | 1 + testbench/jtagdpi/README.md | 1 + testbench/jtagdpi/jtagdpi.c | 2 +- testbench/jtagdpi/jtagdpi.h | 1 + testbench/jtagdpi/jtagdpi.sv | 1 + testbench/tb_top.sv | 2 +- testbench/tcp_server/tcp_server.c | 1 + testbench/tcp_server/tcp_server.h | 1 + tools/Makefile | 1 + tools/openocd/common.tcl | 1 + tools/openocd/jtag_cg.tcl | 1 + tools/openocd/sim-jtagdpi.cfg | 1 + 25 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/scripts/openocd_test.sh b/.github/scripts/openocd_test.sh index dcfc87f4d1f..17fa593a12e 100755 --- a/.github/scripts/openocd_test.sh +++ b/.github/scripts/openocd_test.sh @@ -1,5 +1,6 @@ #!/bin/bash # SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Antmicro # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/scripts/utils.sh b/.github/scripts/utils.sh index 24bd8324275..b3b7bd506ca 100644 --- a/.github/scripts/utils.sh +++ b/.github/scripts/utils.sh @@ -1,5 +1,6 @@ #!/bin/bash # SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Antmicro # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/testbench/ahb_lite_2to1_mux.sv b/testbench/ahb_lite_2to1_mux.sv index 5b9a2d56c60..f55650d5bac 100644 --- a/testbench/ahb_lite_2to1_mux.sv +++ b/testbench/ahb_lite_2to1_mux.sv @@ -1,4 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Antmicro // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/testbench/ahb_sif.sv b/testbench/ahb_sif.sv index d4ef94e7657..d8ba71d9e75 100644 --- a/testbench/ahb_sif.sv +++ b/testbench/ahb_sif.sv @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2019 Western Digital Corporation or its affiliates. -// +// Copyright 2024 Antmicro +// // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/testbench/asm/infinite_loop.s b/testbench/asm/infinite_loop.s index 41037ed264c..ee571ec3dda 100644 --- a/testbench/asm/infinite_loop.s +++ b/testbench/asm/infinite_loop.s @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2019 Western Digital Corporation or its affiliates. +// Copyright 2024 Antmicro // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/testbench/axi4_mux/arbiter.v b/testbench/axi4_mux/arbiter.v index cfac70d1c6d..842b5d77be5 100644 --- a/testbench/axi4_mux/arbiter.v +++ b/testbench/axi4_mux/arbiter.v @@ -1,6 +1,7 @@ /* Copyright (c) 2014-2021 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_crossbar.v b/testbench/axi4_mux/axi_crossbar.v index 991d45403a1..c02cf936ea2 100644 --- a/testbench/axi4_mux/axi_crossbar.v +++ b/testbench/axi4_mux/axi_crossbar.v @@ -1,6 +1,7 @@ /* Copyright (c) 2018 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_crossbar_addr.v b/testbench/axi4_mux/axi_crossbar_addr.v index 7b7846526b2..e240150cebf 100644 --- a/testbench/axi4_mux/axi_crossbar_addr.v +++ b/testbench/axi4_mux/axi_crossbar_addr.v @@ -1,6 +1,7 @@ /* Copyright (c) 2018 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_crossbar_rd.v b/testbench/axi4_mux/axi_crossbar_rd.v index 2b1410ac626..cc43a8c29e4 100644 --- a/testbench/axi4_mux/axi_crossbar_rd.v +++ b/testbench/axi4_mux/axi_crossbar_rd.v @@ -1,6 +1,7 @@ /* Copyright (c) 2018 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_crossbar_wr.v b/testbench/axi4_mux/axi_crossbar_wr.v index 5f556653517..7f733d98275 100644 --- a/testbench/axi4_mux/axi_crossbar_wr.v +++ b/testbench/axi4_mux/axi_crossbar_wr.v @@ -1,6 +1,7 @@ /* Copyright (c) 2018 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_crossbar_wrap_2x1.v b/testbench/axi4_mux/axi_crossbar_wrap_2x1.v index 48a146713c3..8012952cf9e 100644 --- a/testbench/axi4_mux/axi_crossbar_wrap_2x1.v +++ b/testbench/axi4_mux/axi_crossbar_wrap_2x1.v @@ -1,6 +1,7 @@ /* Copyright (c) 2020 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_register_rd.v b/testbench/axi4_mux/axi_register_rd.v index c0df03a03f4..9af8a6dab62 100644 --- a/testbench/axi4_mux/axi_register_rd.v +++ b/testbench/axi4_mux/axi_register_rd.v @@ -1,6 +1,7 @@ /* Copyright (c) 2018 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/axi_register_wr.v b/testbench/axi4_mux/axi_register_wr.v index 9176d6ba956..d8bfba929c3 100644 --- a/testbench/axi4_mux/axi_register_wr.v +++ b/testbench/axi4_mux/axi_register_wr.v @@ -1,6 +1,7 @@ /* Copyright (c) 2018 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/axi4_mux/priority_encoder.v b/testbench/axi4_mux/priority_encoder.v index cf82512ba84..2c1ea966409 100644 --- a/testbench/axi4_mux/priority_encoder.v +++ b/testbench/axi4_mux/priority_encoder.v @@ -1,6 +1,7 @@ /* Copyright (c) 2014-2021 Alex Forencich +Copyright 2024 Antmicro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/testbench/jtagdpi/README.md b/testbench/jtagdpi/README.md index 54750ef1f6b..b712948b6e4 100644 --- a/testbench/jtagdpi/README.md +++ b/testbench/jtagdpi/README.md @@ -1,4 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Antmicro # # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/testbench/jtagdpi/jtagdpi.c b/testbench/jtagdpi/jtagdpi.c index e0b2708748e..51feccc6a75 100644 --- a/testbench/jtagdpi/jtagdpi.c +++ b/testbench/jtagdpi/jtagdpi.c @@ -1,5 +1,5 @@ // Copyright lowRISC contributors. -// Copyright 2023 Antmicro. +// Copyright 2024 Antmicro // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 diff --git a/testbench/jtagdpi/jtagdpi.h b/testbench/jtagdpi/jtagdpi.h index 9ecb2a1044e..584c85f63ea 100644 --- a/testbench/jtagdpi/jtagdpi.h +++ b/testbench/jtagdpi/jtagdpi.h @@ -1,4 +1,5 @@ // Copyright lowRISC contributors. +// Copyright 2024 Antmicro // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 diff --git a/testbench/jtagdpi/jtagdpi.sv b/testbench/jtagdpi/jtagdpi.sv index e69d719b5d6..066fdac2982 100644 --- a/testbench/jtagdpi/jtagdpi.sv +++ b/testbench/jtagdpi/jtagdpi.sv @@ -1,4 +1,5 @@ // Copyright lowRISC contributors. +// Copyright 2024 Antmicro // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index d96dae08c8e..ae795d4b1a5 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2019 Western Digital Corporation or its affiliates. -// Copyright (c) 2023 Antmicro +// Copyright (c) 2024 Antmicro // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/testbench/tcp_server/tcp_server.c b/testbench/tcp_server/tcp_server.c index c0abb95c040..12af3637ae3 100644 --- a/testbench/tcp_server/tcp_server.c +++ b/testbench/tcp_server/tcp_server.c @@ -1,4 +1,5 @@ // Copyright lowRISC contributors. +// Copyright 2024 Antmicro // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 diff --git a/testbench/tcp_server/tcp_server.h b/testbench/tcp_server/tcp_server.h index 5226322f90a..0d8293c212b 100644 --- a/testbench/tcp_server/tcp_server.h +++ b/testbench/tcp_server/tcp_server.h @@ -1,4 +1,5 @@ // Copyright lowRISC contributors. +// Copyright 2024 Antmicro // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 diff --git a/tools/Makefile b/tools/Makefile index 969456af6e4..6d5d9515adc 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2020 Western Digital Corporation or its affiliates. +# Copyright 2024 Antmicro # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/openocd/common.tcl b/tools/openocd/common.tcl index 46017011fe6..00c2db1d925 100644 --- a/tools/openocd/common.tcl +++ b/tools/openocd/common.tcl @@ -1,4 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Antmicro # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/openocd/jtag_cg.tcl b/tools/openocd/jtag_cg.tcl index e15b0bb7c6d..51291eecd4d 100644 --- a/tools/openocd/jtag_cg.tcl +++ b/tools/openocd/jtag_cg.tcl @@ -1,4 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 Antmicro # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tools/openocd/sim-jtagdpi.cfg b/tools/openocd/sim-jtagdpi.cfg index 7d43a4ad5e3..bdd355b1a52 100644 --- a/tools/openocd/sim-jtagdpi.cfg +++ b/tools/openocd/sim-jtagdpi.cfg @@ -1,4 +1,5 @@ # Copyright lowRISC contributors. +# Copyright 2024 Antmicro # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 From 5a32c70e8faae5f340c1d947edac022881d2aca0 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 11 Jul 2024 15:51:37 +0200 Subject: [PATCH 08/14] Move tools/openocd to testbench/openocd_scripts Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/scripts/openocd_test.sh | 2 +- .github/workflows/test-openocd.yml | 4 ++-- {tools/openocd => testbench/openocd_scripts}/common.tcl | 0 {tools/openocd => testbench/openocd_scripts}/jtag_cg.tcl | 0 {tools/openocd => testbench/openocd_scripts}/sim-jtagdpi.cfg | 0 {tools/openocd => testbench/openocd_scripts}/veer-el2-rst.cfg | 0 .../openocd => testbench/openocd_scripts}/verilator-rst.cfg | 0 7 files changed, 3 insertions(+), 3 deletions(-) rename {tools/openocd => testbench/openocd_scripts}/common.tcl (100%) rename {tools/openocd => testbench/openocd_scripts}/jtag_cg.tcl (100%) rename {tools/openocd => testbench/openocd_scripts}/sim-jtagdpi.cfg (100%) rename {tools/openocd => testbench/openocd_scripts}/veer-el2-rst.cfg (100%) rename {tools/openocd => testbench/openocd_scripts}/verilator-rst.cfg (100%) diff --git a/.github/scripts/openocd_test.sh b/.github/scripts/openocd_test.sh index 17fa593a12e..29332e35463 100755 --- a/.github/scripts/openocd_test.sh +++ b/.github/scripts/openocd_test.sh @@ -59,7 +59,7 @@ sleep 2s # Run the test echo -e "${COLOR_WHITE}======== Running OpenOCD test '$@' ========${COLOR_OFF}" -cd ${RV_ROOT}/tools/openocd && openocd -d2 ${OPENOCD_ARGS} >"${OPENOCD_LOG}" 2>&1 +cd ${RV_ROOT}/testbench/openocd_scripts && openocd -d2 ${OPENOCD_ARGS} >"${OPENOCD_LOG}" 2>&1 EXITCODE=$? if [ ${EXITCODE} -eq 0 ]; then diff --git a/.github/workflows/test-openocd.yml b/.github/workflows/test-openocd.yml index 3583fe86900..4f27a3b1318 100644 --- a/.github/workflows/test-openocd.yml +++ b/.github/workflows/test-openocd.yml @@ -78,5 +78,5 @@ jobs: make -C run -f ${RV_ROOT}/tools/Makefile verilator-build program.hex TEST=infinite_loop cd run ${RV_ROOT}/.github/scripts/openocd_test.sh \ - -f ${RV_ROOT}/tools/openocd/verilator-rst.cfg \ - -f ${RV_ROOT}/tools/openocd/jtag_cg.tcl + -f ${RV_ROOT}/testbench/openocd_scripts/verilator-rst.cfg \ + -f ${RV_ROOT}/testbench/openocd_scripts/jtag_cg.tcl diff --git a/tools/openocd/common.tcl b/testbench/openocd_scripts/common.tcl similarity index 100% rename from tools/openocd/common.tcl rename to testbench/openocd_scripts/common.tcl diff --git a/tools/openocd/jtag_cg.tcl b/testbench/openocd_scripts/jtag_cg.tcl similarity index 100% rename from tools/openocd/jtag_cg.tcl rename to testbench/openocd_scripts/jtag_cg.tcl diff --git a/tools/openocd/sim-jtagdpi.cfg b/testbench/openocd_scripts/sim-jtagdpi.cfg similarity index 100% rename from tools/openocd/sim-jtagdpi.cfg rename to testbench/openocd_scripts/sim-jtagdpi.cfg diff --git a/tools/openocd/veer-el2-rst.cfg b/testbench/openocd_scripts/veer-el2-rst.cfg similarity index 100% rename from tools/openocd/veer-el2-rst.cfg rename to testbench/openocd_scripts/veer-el2-rst.cfg diff --git a/tools/openocd/verilator-rst.cfg b/testbench/openocd_scripts/verilator-rst.cfg similarity index 100% rename from tools/openocd/verilator-rst.cfg rename to testbench/openocd_scripts/verilator-rst.cfg From 53bc3ed4367250b7bc09d0ba7bde3041d9c19f6d Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 12 Jul 2024 12:12:51 +0200 Subject: [PATCH 09/14] Handle end of simulation Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/scripts/openocd_test.sh | 8 ++------ testbench/tb_top.sv | 3 +++ testbench/test_tb_top.cpp | 1 + tools/Makefile | 2 +- tools/riscv-dv/Makefile | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/scripts/openocd_test.sh b/.github/scripts/openocd_test.sh index 29332e35463..e1e54867e63 100755 --- a/.github/scripts/openocd_test.sh +++ b/.github/scripts/openocd_test.sh @@ -68,15 +68,11 @@ else echo -e "${COLOR_RED}[FAILED]${COLOR_OFF}" fi -sleep 1s - -# Terminate -echo -e "${COLOR_WHITE}Terminating...${COLOR_OFF}" -terminate ${SIM_PID} - # Display logs print_logs +wait $SIM_PID + # Honor the exitcode exit ${EXITCODE} diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index ae795d4b1a5..5279faccfaa 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -726,6 +726,9 @@ module tb_top $display("TEST_PASSED"); $display("\nFinished : minstret = %0d, mcycle = %0d", `DEC.tlu.minstretl[31:0],`DEC.tlu.mcyclel[31:0]); $display("See \"exec.log\" for execution trace with register updates..\n"); + // OpenOCD test breaks if simulation closes the TCP connection first. + // This delay allows OpenOCD to close the connection before the #finish. + #15000; $finish; end else if(mailbox_write && mailbox_data[7:0] == 8'h1) begin diff --git a/testbench/test_tb_top.cpp b/testbench/test_tb_top.cpp index 4fa70b5d913..fbebf3a4a79 100644 --- a/testbench/test_tb_top.cpp +++ b/testbench/test_tb_top.cpp @@ -147,6 +147,7 @@ int main(int argc, char** argv) { tb->core_clk = !tb->core_clk; tb->eval(); } + tb->final(); #if VM_TRACE tfp->close(); diff --git a/tools/Makefile b/tools/Makefile index 6d5d9515adc..f3e78b95327 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -175,7 +175,7 @@ verilator-build: ${TBFILES} ${BUILD_DIR}/defines.h $(TB_VERILATOR_SRCS) $(VERILATOR) --cc -CFLAGS "${CFLAGS}" $(defines) \ $(includes) -I${RV_ROOT}/testbench -f ${RV_ROOT}/testbench/flist \ $(VERILATOR_SKIP_WARNINGS) ${TBFILES} --top-module tb_top \ - -exe $(TB_VERILATOR_SRCS) --autoflush $(VERILATOR_DEBUG) $(VERILATOR_COVERAGE) + -exe $(TB_VERILATOR_SRCS) --autoflush --timing $(VERILATOR_DEBUG) $(VERILATOR_COVERAGE) cp ${RV_ROOT}/testbench/test_tb_top.cpp obj_dir/ $(MAKE) -e -C obj_dir/ -f Vtb_top.mk $(VERILATOR_MAKE_FLAGS) touch verilator-build diff --git a/tools/riscv-dv/Makefile b/tools/riscv-dv/Makefile index 878e7a6e37e..0ce5f581250 100644 --- a/tools/riscv-dv/Makefile +++ b/tools/riscv-dv/Makefile @@ -95,7 +95,7 @@ $(WORK_DIR)/verilator/Vtb_top.mk: $(WORK_DIR)/defines.h $(VERILATOR) --cc -CFLAGS $(VERILATOR_CFLAGS) $(VERILATOR_INC) \ $(HDL_FILES) -f $(RV_ROOT)/testbench/flist --top-module tb_top \ -exe $(VERILATOR_EXE) -Wno-WIDTH -Wno-UNOPTFLAT $(VERILATOR_NOIMPLICIT) --autoflush \ - $(VERILATOR_COVERAGE) \ + --timing $(VERILATOR_COVERAGE) \ -Mdir $(WORK_DIR)/verilator $(WORK_DIR)/verilator/Vtb_top: $(WORK_DIR)/verilator/Vtb_top.mk From 91864596373622d7fba58541c235d285480b4f87 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 17 Jul 2024 15:59:09 +0200 Subject: [PATCH 10/14] Add ifdefs Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/workflows/test-openocd.yml | 3 +- configs/veer.config | 4 ++ testbench/tb_top.sv | 63 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-openocd.yml b/.github/workflows/test-openocd.yml index 4f27a3b1318..2011bbf334d 100644 --- a/.github/workflows/test-openocd.yml +++ b/.github/workflows/test-openocd.yml @@ -75,7 +75,8 @@ jobs: export PATH=/opt/verilator/bin:/opt/openocd/bin:$PATH export RV_ROOT=$(pwd) mkdir run - make -C run -f ${RV_ROOT}/tools/Makefile verilator-build program.hex TEST=infinite_loop + make -C run -f ${RV_ROOT}/tools/Makefile verilator-build program.hex TEST=infinite_loop \ + CONF_PARAMS="-set build_axi4 -set openocd_test" cd run ${RV_ROOT}/.github/scripts/openocd_test.sh \ -f ${RV_ROOT}/testbench/openocd_scripts/verilator-rst.cfg \ diff --git a/configs/veer.config b/configs/veer.config index 9726e824b30..d0a4e55eeb3 100755 --- a/configs/veer.config +++ b/configs/veer.config @@ -272,6 +272,7 @@ my $fast_interrupt_redirect = 1; # ON by default my $lsu_num_nbload=4; my $ahb = 0; my $axi = 1; +my $openocd_test = 0; my $text_in_iccm = 0; my $lsu2dma = 0; @@ -1045,6 +1046,7 @@ our %config = (#{{{ "build_ahb_lite" => "$ahb", "build_axi4" => "$axi", "build_axi_native" => "1", + "openocd_test" => "$openocd_test", "ext_datawidth" => "64", "ext_addrwidth" => "32", "sterr_rollback" => "0", @@ -1929,6 +1931,8 @@ if (defined($config{"testbench"}{"build_axi_native"}) && ($config{"testbench"}{" delete $config{core}{fpga_optimize} if ($config{core}{fpga_optimize} == 0); +delete $config{testbench}{openocd_test} if ($config{testbench}{openocd_test} == 0); + # Remove TECH_SPECIFIC_* defines if they are set to 0 foreach my $key (sort keys(%config)) { if (grep(/tech_specific_/, $key)) { diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index 5279faccfaa..1a2ebb4db25 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -151,6 +151,7 @@ module tb_top logic [11:0] wb_csr_dest; logic [31:0] wb_csr_data; + `ifdef RV_OPENOCD_TEST // SB and LSU AHB master mux ahb_lite_2to1_mux #( .AHB_LITE_ADDR_WIDTH (32), @@ -196,6 +197,19 @@ module tb_top .hreadyout_i (mux_hreadyout), .hrdata_i (mux_hrdata) ); + `else + assign mux_hsel = 1'b1; + assign mux_haddr = lsu_haddr; + assign mux_hwdata = lsu_hwdata; + assign mux_hwrite = lsu_hwrite; + assign mux_htrans = lsu_htrans; + assign mux_hsize = lsu_hsize; + assign mux_hready = lsu_hready; + + assign lsu_hresp = mux_hresp; + assign lsu_hrdata = mux_hrdata; + assign lsu_hready = mux_hreadyout; + `endif `ifdef RV_BUILD_AXI4 //-------------------------- LSU AXI signals-------------------------- @@ -466,6 +480,7 @@ module tb_top wire mux_axi_awregion; wire mux_axi_arregion; +`ifdef RV_OPENOCD_TEST axi_crossbar_wrap_2x1 #( .ADDR_WIDTH (32), .DATA_WIDTH (64), @@ -621,6 +636,52 @@ module tb_top .m00_axi_awregion(mux_axi_awregion), .m00_axi_arregion(mux_axi_arregion) ); +`else + assign mux_axi_arvalid = lsu_axi_arvalid; + assign lsu_axi_arready = mux_axi_arready; + assign mux_axi_araddr = lsu_axi_araddr; + assign mux_axi_arid = lsu_axi_arid; + assign mux_axi_arlen = lsu_axi_arlen; + assign mux_axi_arburst = lsu_axi_arburst; + assign mux_axi_arsize = lsu_axi_arsize; + assign lsu_axi_rvalid = mux_axi_rvalid; + assign mux_axi_rready = lsu_axi_rready; + assign lsu_axi_rdata = mux_axi_rdata; + assign lsu_axi_rresp = mux_axi_rresp; + assign lsu_axi_rid = mux_axi_rid; + assign lsu_axi_rlast = mux_axi_rlast; + assign mux_axi_awvalid = lsu_axi_awvalid; + assign lsu_axi_awready = mux_axi_awready; + assign mux_axi_awaddr = lsu_axi_awaddr; + assign mux_axi_awid = lsu_axi_awid; + assign mux_axi_awlen = lsu_axi_awlen; + assign mux_axi_awburst = lsu_axi_awburst; + assign mux_axi_awlock = lsu_axi_awlock; + assign mux_axi_awcache = lsu_axi_awcache; + assign mux_axi_awprot = lsu_axi_awprot; + assign mux_axi_awqos = lsu_axi_awqos; + assign mux_axi_awuser = lsu_axi_awuser; + assign mux_axi_wlast = lsu_axi_wlast; + assign mux_axi_wuser = lsu_axi_wuser; + assign lsu_axi_buser = mux_axi_buser; + assign mux_axi_arlock = lsu_axi_arlock; + assign mux_axi_arcache = lsu_axi_arcache; + assign mux_axi_arprot = lsu_axi_arprot; + assign mux_axi_arqos = lsu_axi_arqos; + assign mux_axi_aruser = lsu_axi_aruser; + assign lsu_axi_ruser = mux_axi_ruser; + assign mux_axi_awsize = lsu_axi_awsize; + assign mux_axi_wdata = lsu_axi_wdata; + assign mux_axi_wstrb = lsu_axi_wstrb; + assign mux_axi_wvalid = lsu_axi_wvalid; + assign lsu_axi_wready = mux_axi_wready; + assign lsu_axi_bvalid = mux_axi_bvalid; + assign mux_axi_bready = lsu_axi_bready; + assign lsu_axi_bresp = mux_axi_bresp; + assign lsu_axi_bid = mux_axi_bid; + assign mux_axi_awregion = lsu_axi_awregion; + assign mux_axi_arregion = lsu_axi_arregion; +`endif `endif string abi_reg[32]; // ABI register names @@ -2126,6 +2187,7 @@ for (genvar i=0; i Date: Thu, 18 Jul 2024 13:47:15 +0200 Subject: [PATCH 11/14] Fix formatting Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- testbench/tb_top.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index 1a2ebb4db25..6e73f58c6aa 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -72,7 +72,7 @@ module tb_top logic [63:0] lsu_hrdata ; logic [63:0] lsu_hwdata ; logic lsu_hready ; - logic lsu_hresp ; + logic lsu_hresp ; logic [31:0] mux_haddr ; logic [2:0] mux_hburst ; @@ -81,12 +81,12 @@ module tb_top logic [2:0] mux_hsize ; logic [1:0] mux_htrans ; logic mux_hwrite ; - logic mux_hsel ; + logic mux_hsel ; logic [63:0] mux_hrdata ; logic [63:0] mux_hwdata ; logic mux_hready ; logic mux_hresp ; - logic mux_hreadyout ; + logic mux_hreadyout ; logic [31:0] sb_haddr ; logic [2:0] sb_hburst ; From 0376fb106eba19eb6287a573a77aedc133d0a029 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 19 Jul 2024 10:00:15 +0200 Subject: [PATCH 12/14] Gather coverage Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/workflows/report-coverage.yml | 6 ++++++ .github/workflows/test-openocd.yml | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/report-coverage.yml b/.github/workflows/report-coverage.yml index 4b39a077790..e030b515000 100644 --- a/.github/workflows/report-coverage.yml +++ b/.github/workflows/report-coverage.yml @@ -66,6 +66,12 @@ jobs: name: riscof_coverage_data path: ./ + - name: Download coverage reports + uses: actions/download-artifact@v3 + with: + name: openocd_coverage_data + path: ./ + - name: Generate reports run: | export PATH=${{ env.LCOV_PATH }}:${PATH} diff --git a/.github/workflows/test-openocd.yml b/.github/workflows/test-openocd.yml index 2011bbf334d..70c1ec73d6b 100644 --- a/.github/workflows/test-openocd.yml +++ b/.github/workflows/test-openocd.yml @@ -76,8 +76,25 @@ jobs: export RV_ROOT=$(pwd) mkdir run make -C run -f ${RV_ROOT}/tools/Makefile verilator-build program.hex TEST=infinite_loop \ - CONF_PARAMS="-set build_axi4 -set openocd_test" + CONF_PARAMS="-set build_axi4 -set openocd_test" COVERAGE=${{ matrix.coverage }} cd run ${RV_ROOT}/.github/scripts/openocd_test.sh \ -f ${RV_ROOT}/testbench/openocd_scripts/verilator-rst.cfg \ -f ${RV_ROOT}/testbench/openocd_scripts/jtag_cg.tcl + + - name: Prepare coverage data + run: | + export PATH=/opt/verilator/bin:$PATH + export RV_ROOT=$(pwd) + .github/scripts/convert_coverage_data.sh ${RV_ROOT}/run + echo "convert_coverage_data.sh exited with RET_CODE = "$? + mkdir -p results + mv ${RV_ROOT}/run/coverage.info \ + results/coverage_openocd_${{ matrix.coverage }}.info + + - name: Pack artifacts + if: always() + uses: actions/upload-artifact@v3 + with: + name: openocd_coverage_data + path: results/*.info From b9f67d75b0f3259572eed8516d5837092cd4fd2f Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 19 Jul 2024 11:32:52 +0200 Subject: [PATCH 13/14] Test openocd with ahb_lite bus Internal-tag: [#60812] Signed-off-by: Ryszard Rozak --- .github/workflows/test-openocd.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-openocd.yml b/.github/workflows/test-openocd.yml index 70c1ec73d6b..0f5ea30517d 100644 --- a/.github/workflows/test-openocd.yml +++ b/.github/workflows/test-openocd.yml @@ -12,6 +12,7 @@ jobs: fail-fast: false matrix: coverage: ["all", "branch", "toggle"] #TODO: add functional coverage + bus: ["axi4", "ahb_lite"] env: DEBIAN_FRONTEND: "noninteractive" CCACHE_DIR: "/opt/openocd-tests/.cache/" @@ -76,7 +77,7 @@ jobs: export RV_ROOT=$(pwd) mkdir run make -C run -f ${RV_ROOT}/tools/Makefile verilator-build program.hex TEST=infinite_loop \ - CONF_PARAMS="-set build_axi4 -set openocd_test" COVERAGE=${{ matrix.coverage }} + CONF_PARAMS="-set build_${{ matrix.bus }} -set openocd_test" COVERAGE=${{ matrix.coverage }} cd run ${RV_ROOT}/.github/scripts/openocd_test.sh \ -f ${RV_ROOT}/testbench/openocd_scripts/verilator-rst.cfg \ @@ -90,7 +91,7 @@ jobs: echo "convert_coverage_data.sh exited with RET_CODE = "$? mkdir -p results mv ${RV_ROOT}/run/coverage.info \ - results/coverage_openocd_${{ matrix.coverage }}.info + results/coverage_openocd_${{ matrix.bus }}_${{ matrix.coverage }}.info - name: Pack artifacts if: always() From 4ba3e1a317ebc092c960a54cec096dbef79c11b9 Mon Sep 17 00:00:00 2001 From: Aleksander Kiryk Date: Fri, 26 Jul 2024 10:28:32 +0200 Subject: [PATCH 14/14] Remove redeclared signals Internal-tag: [#60812] --- testbench/tb_top.sv | 5 ----- 1 file changed, 5 deletions(-) diff --git a/testbench/tb_top.sv b/testbench/tb_top.sv index 6e73f58c6aa..e402078716b 100644 --- a/testbench/tb_top.sv +++ b/testbench/tb_top.sv @@ -260,7 +260,6 @@ module tb_top wire [1:0] lsu_axi_rresp; wire lsu_axi_rlast; wire lsu_axi_awuser; - wire lsu_axi_wlast; wire lsu_axi_wuser; wire lsu_axi_buser; wire lsu_axi_aruser; @@ -360,7 +359,6 @@ module tb_top wire [1:0] sb_axi_rresp; wire sb_axi_rlast; wire sb_axi_awuser; - wire sb_axi_wlast; wire sb_axi_wuser; wire sb_axi_buser; wire sb_axi_aruser; @@ -472,13 +470,10 @@ module tb_top wire [1:0] mux_axi_rresp; wire mux_axi_rlast; wire mux_axi_awuser; - wire mux_axi_wlast; wire mux_axi_wuser; wire mux_axi_buser; wire mux_axi_aruser; wire mux_axi_ruser; - wire mux_axi_awregion; - wire mux_axi_arregion; `ifdef RV_OPENOCD_TEST axi_crossbar_wrap_2x1 #(