├── tb ├── common_noc │ ├── __init__.py │ ├── ravenoc_pkt.py │ ├── constants.py │ └── testbench.py ├── test_ravenoc_basic.py ├── test_max_data.py ├── test_throughput.py ├── test_virt_chn_qos.py ├── test_all_buffers.py ├── test_wrong_ops.py ├── test_noc_csr.py └── test_irqs.py ├── docs └── img │ └── raven_logo.png ├── .ecrc ├── TODO ├── .gitmodules ├── utils ├── dump_all_xcelium.tcl ├── run_verible.sh ├── ravenoc_coffee.gtkw ├── ravenoc_vanilla.gtkw ├── restore.tcl └── ravenoc_basic_vanilla.gtkw ├── src ├── include │ ├── ravenoc_pkg.sv │ ├── ravenoc_axi_fnc.svh │ ├── ravenoc_defines.svh │ └── ravenoc_structs.svh ├── router │ ├── router_if.sv │ ├── input_module.sv │ ├── rr_arbiter.sv │ ├── vc_buffer.sv │ ├── fifo.sv │ ├── input_datapath.sv │ ├── input_router.sv │ ├── output_module.sv │ └── router_ravenoc.sv ├── ni │ ├── pkt_proc.sv │ ├── cdc_pkt.sv │ ├── router_wrapper.sv │ ├── async_gp_fifo.sv │ └── axi_csr.sv ├── ravenoc.sv └── ravenoc_wrapper.sv ├── .github ├── verible-lint-matcher.json └── workflows │ ├── lint_editor.yaml │ ├── lint_sv.yaml │ └── regression.yaml ├── .gitignore ├── .editorconfig ├── Makefile ├── tox.ini ├── LICENSE ├── Dockerfile.ravenoc └── ravenoc.core /tb/common_noc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/img/raven_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aignacio/ravenoc/HEAD/docs/img/raven_logo.png -------------------------------------------------------------------------------- /.ecrc: -------------------------------------------------------------------------------- 1 | { 2 | "Exclude": ["\\.py$", "\\.yaml$", "\\.md$", "\\.core$", "\\.tcl$", "\\.gitmodules$"] 3 | } 4 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - bring axi vip log to dut logs 2 | - add version to the header log 3 | - add workflow for linting 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bus_arch_sv_pkg"] 2 | path = bus_arch_sv_pkg 3 | url = git@github.com:aignacio/bus_arch_sv_pkg.git 4 | -------------------------------------------------------------------------------- /utils/dump_all_xcelium.tcl: -------------------------------------------------------------------------------- 1 | probe -create -shm -depth all -variables -ports -uvm -all -generics -functions -tasks -dynamic -unpacked 0 2 | -------------------------------------------------------------------------------- /src/include/ravenoc_pkg.sv: -------------------------------------------------------------------------------- 1 | `ifndef _RAVENOC_PKG_ 2 | `define _RAVENOC_PKG_ 3 | package ravenoc_pkg; 4 | `include "ravenoc_defines.svh" 5 | `include "ravenoc_structs.svh" 6 | `include "ravenoc_axi_fnc.svh" 7 | endpackage 8 | `endif 9 | -------------------------------------------------------------------------------- /utils/run_verible.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | ROOT=$(cd "$(dirname "$0}")/.." && pwd) 4 | 5 | find $ROOT/src/ \ 6 | -iname "*.sv" \ 7 | -o -iname "*.svh" \ 8 | | xargs verible-verilog-lint \ 9 | --lint_fatal --parse_fatal 10 | -------------------------------------------------------------------------------- /.github/verible-lint-matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "verible-lint-matcher", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.+):(\\d+):(\\d+):\\s(.+)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "message": 4 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /**/*.o 2 | /**/*.baremetal 3 | /**/*.asm 4 | obj 5 | obj_dir 6 | output_icarus 7 | output_verilator 8 | sim 9 | output 10 | INCA_libs 11 | *.log 12 | irun.* 13 | xrun.* 14 | .simvision 15 | waves.shm 16 | xcelium.shm 17 | ***.swp 18 | tb/sim_build* 19 | __pycache__ 20 | results.xml 21 | dump.fst 22 | run_dir 23 | build 24 | fusesoc.conf 25 | .tox 26 | .test_durations 27 | bin 28 | *.tar.gz 29 | -------------------------------------------------------------------------------- /.github/workflows/lint_editor.yaml: -------------------------------------------------------------------------------- 1 | name: lint-editorconfig 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | editorconfig: 6 | name: Editorconfig 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-python@v2 11 | with: 12 | python-version: 3.x 13 | - name: Install requirements 14 | run: pip install editorconfig-checker 15 | - name: Run editorconfig checker 16 | run: | 17 | ec -exclude .gitmodules 18 | 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | max_line_length = 120 12 | 13 | [{*.md,*.rst}] 14 | trim_trailing_whitespace = false 15 | 16 | [{Makefile,*.mk,*.bat}] 17 | indent_style = tab 18 | indent_size = 2 19 | 20 | [*.{c,h,cpp,hpp}] 21 | indent_size = 2 22 | 23 | [*.{sv,svh,v,vhl}] 24 | indent_size = 2 25 | indent_tab = space 26 | 27 | [*.py] 28 | indent_style = space 29 | indent_size = 4 30 | 31 | [**.js] 32 | indent_style = space 33 | indent_size = 2 34 | 35 | [{*.json,*.yml}] 36 | indent_style = space 37 | indent_size = 2 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # File : Makefile 2 | # License : MIT license 3 | # Author : Anderson Ignacio da Silva (aignacio) 4 | # Date : 07.06.2022 5 | # Last Modified Date: 27.12.2022 6 | COV_REP := $(shell find run_dir -name 'coverage.dat') 7 | SPEC_TEST ?= -k test_ravenoc_basic['vanilla'] 8 | RUN_CMD := docker run --rm --name ravenoc \ 9 | -v $(abspath .):/ravenoc -w \ 10 | /ravenoc aignacio/ravenoc 11 | 12 | .PHONY: run cov clean all 13 | 14 | all: run 15 | @echo ">Test run finished, please check the terminal" 16 | 17 | run: 18 | $(RUN_CMD) tox -- $(SPEC_TEST) 19 | 20 | coverage.info: 21 | verilator_coverage $(COV_REP) --write-info coverage.info 22 | 23 | cov: coverage.info 24 | genhtml $< -o output_lcov 25 | 26 | clean: 27 | $(RUN_CMD) rm -rf run_dir 28 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = True 3 | envlist = py39 4 | 5 | [gh-actions] 6 | python = 3.9: py39 7 | 8 | [testenv] 9 | setenv = 10 | SIM = verilator 11 | DUT = ravenoc_wrapper 12 | TIMEUNIT = "1ns" 13 | TIMEPREC = "100ps" 14 | #FULL_REGRESSION = 1 15 | #HOME = $HOME 16 | #XRUNOPTS="-debugscript /home/user/dbg.csh" 17 | 18 | deps = 19 | pytest 20 | pytest-xdist 21 | pytest-split 22 | cocotb-bus == 0.1.1 23 | cocotbext-axi == 0.1.10 24 | cocotb-test == 0.2.0 25 | cocotb == 1.5.1 26 | 27 | # For dev purposes 28 | #git+https://github.com/alexforencich/cocotbext-axi.git@master 29 | #git+https://github.com/cocotb/cocotb.git@stable/1.5 30 | #git+https://github.com/aignacio/cocotb-test.git@origin/cocotb_1p5 31 | 32 | commands = pytest -n auto {posargs} 33 | 34 | #pytest -rP -n auto {posargs} 35 | 36 | [pytest] 37 | testpaths = tb 38 | addopts = --import-mode prepend 39 | -------------------------------------------------------------------------------- /.github/workflows/lint_sv.yaml: -------------------------------------------------------------------------------- 1 | name: lint-sv 2 | on: [push, pull_request] 3 | env: 4 | VERIBLE_VERSION: 0.0-1051-gd4cd328 5 | 6 | jobs: 7 | system_verilog: 8 | name: System Verilog Sources 9 | runs-on: ubuntu-20.04 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Install Verible 13 | run: | 14 | set -e 15 | mkdir -p build/verible 16 | cd build/verible 17 | curl -Ls -o verible.tar.gz https://github.com/google/verible/releases/download/v$VERIBLE_VERSION/verible-v$VERIBLE_VERSION-Ubuntu-20.04-focal-x86_64.tar.gz 18 | sudo mkdir -p /tools/verible && sudo chmod 777 /tools/verible 19 | tar -C /tools/verible -xf verible.tar.gz --strip-components=1 20 | echo "PATH=$PATH:/tools/verible/bin" >> $GITHUB_ENV 21 | #https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md#problem-matchers 22 | - name: Run Lint Verible 23 | run: | 24 | echo "::add-matcher::.github/verible-lint-matcher.json" 25 | utils/run_verible.sh 26 | echo "::remove-matcher owner=verible-lint-matcher::" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Anderson Ignacio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Dockerfile.ravenoc: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | LABEL author="Anderson Ignacio da Silva" 3 | LABEL maintainer="anderson@aignacio.com" 4 | ENV TZ=Europe/Dublin 5 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 6 | RUN apt-get update && apt-get upgrade -y 7 | RUN apt install software-properties-common -y 8 | #RUN add-apt-repository ppa:deadsnakes/ppa 9 | #RUN apt install python3.9 -y 10 | RUN apt-get install python3.9 python3.9-dev python3.9-distutils make g++ \ 11 | perl autoconf flex bison libfl2 libfl-dev zlibc \ 12 | zlib1g zlib1g-dev git file gcc \ 13 | make time wget zip python3-pip -y 14 | # [Verilator] 15 | RUN git clone https://github.com/verilator/verilator 16 | WORKDIR /verilator 17 | RUN export VERILATOR_ROOT=/verilator 18 | RUN git checkout v4.106 # Update latest stable 19 | RUN autoconf # Create ./configure script 20 | RUN ./configure # Configure and create Makefile 21 | RUN make -j4 # Build Verilator itself (if error, try just 'make') 22 | RUN make install 23 | # [Python] 24 | RUN pip install tox 25 | -------------------------------------------------------------------------------- /.github/workflows/regression.yaml: -------------------------------------------------------------------------------- 1 | name: Regression Tests 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | name: Tox (pytest) - ${{ matrix.os }} / ${{ matrix.python-version }} 7 | runs-on: ${{ matrix.os }} 8 | 9 | strategy: 10 | matrix: 11 | os: [ubuntu-20.04] 12 | python-version: ['3.9'] 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | submodules: 'recursive' 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install Verilator 23 | run: | 24 | sudo apt install -y --no-install-recommends make g++ perl lcov python3 autoconf flex bison libfl2 libfl-dev zlibc zlib1g zlib1g-dev 25 | git clone https://github.com/verilator/verilator.git 26 | cd verilator 27 | git checkout v4.106 28 | autoconf 29 | ./configure 30 | make -j $(nproc) 31 | sudo make install 32 | - name: Install Python dependencies 33 | run: | 34 | python -m pip install --upgrade pip 35 | pip install tox tox-gh-actions 36 | - name: Test with tox 37 | run: tox 38 | 39 | - name: Generate coverage 40 | run: make cov RUN_CMD= 41 | 42 | - name: Upload coverage reports to Codecov 43 | uses: codecov/codecov-action@v3 44 | env: 45 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 46 | files: ./coverage.info 47 | verbose: true 48 | -------------------------------------------------------------------------------- /src/router/router_if.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: router_if.sv 3 | * Description: RaveNoC interface for routers 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | interface router_if; 27 | import amba_axi_pkg::*; 28 | import ravenoc_pkg::*; 29 | 30 | s_flit_req_t req; 31 | s_flit_resp_t resp; 32 | 33 | modport send_flit ( 34 | output req, 35 | input resp 36 | ); 37 | 38 | modport recv_flit ( 39 | input req, 40 | output resp 41 | ); 42 | 43 | endinterface 44 | -------------------------------------------------------------------------------- /ravenoc.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | name: ravenoc:noc:v1.0.1 3 | description: RaveNoC is a configurable HDL for mesh NoCs topology that allows the user to change parameters and setup new configurations 4 | 5 | filesets: 6 | rtl: 7 | files: 8 | - src/include/ravenoc_axi_fnc.svh: {is_include_file: true} 9 | - src/include/ravenoc_defines.svh: {is_include_file: true} 10 | - bus_arch_sv_pkg/amba_axi_pkg.sv 11 | - src/include/ravenoc_structs.svh: {is_include_file: true} 12 | - src/include/ravenoc_pkg.sv 13 | - src/ni/axi_csr.sv 14 | - src/ni/axi_slave_if.sv 15 | - src/ni/router_wrapper.sv 16 | - src/ni/async_gp_fifo.sv 17 | - src/ni/cdc_pkt.sv 18 | - src/ni/pkt_proc.sv 19 | - src/ravenoc.sv 20 | - src/router/fifo.sv 21 | - src/router/output_module.sv 22 | - src/router/router_if.sv 23 | - src/router/router_ravenoc.sv 24 | - src/router/rr_arbiter.sv 25 | - src/router/vc_buffer.sv 26 | - src/router/input_router.sv 27 | - src/router/input_module.sv 28 | - src/router/input_datapath.sv 29 | file_type: systemVerilogSource 30 | 31 | tb: 32 | files: 33 | - src/ravenoc_wrapper.sv 34 | file_type: systemVerilogSource 35 | 36 | targets: 37 | default: &default 38 | filesets: [rtl] 39 | toplevel: ravenoc 40 | #parameters: [DEBUG=1] 41 | 42 | lint: 43 | default_tool: verilator 44 | filesets: [rtl] 45 | tools: 46 | verilator: 47 | mode: lint-only 48 | verilator_options: ["--Wno-UNOPTFLAT"] 49 | toplevel: ravenoc 50 | 51 | #sim: 52 | #<<: *default 53 | #default_tool: verilator 54 | #description: Simple verilator sim of the NoC 55 | #filesets_append: [tb] 56 | #tools: 57 | #verilator: 58 | #mode: cc 59 | #verilator_options: ["--trace-fst", "--coverage", "--trace-structs", "--Wno-UNOPTFLAT", "--Wno-REDEFMACRO", "--timescale 1ns/100ps"] 60 | #toplevel: ravenoc_wrapper 61 | 62 | parameters: 63 | AXI_CDC_REQ: 64 | datatype : int 65 | description : Add/Remove CDC component from the NoC router 66 | paramtype : vlogparam 67 | DEBUG: 68 | datatype : int 69 | description : -- 70 | paramtype : vlogparam 71 | 72 | provider: 73 | name : github 74 | user : aignacio 75 | repo : ravenoc 76 | version: v1.0.1 77 | -------------------------------------------------------------------------------- /src/router/input_module.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: input_module.sv 3 | * Description: Input module that has the structural connection 4 | * between datapath with the flit's fifos and the 5 | * router that maps to different output modules. 6 | * Author: Anderson Ignacio da Silva 7 | * 8 | * MIT License 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | module input_module 28 | import amba_axi_pkg::*; 29 | import ravenoc_pkg::*; 30 | #( 31 | parameter logic [XWidth-1:0] ROUTER_X_ID = 0, 32 | parameter logic [YWidth-1:0] ROUTER_Y_ID = 0 33 | )( 34 | input clk, 35 | input arst, 36 | // Input interface - from external 37 | input s_flit_req_t fin_req_i, 38 | output s_flit_resp_t fin_resp_o, 39 | // Output Interface - Output module 40 | output s_flit_req_t fout_req_o, 41 | input s_flit_resp_t fout_resp_i, 42 | output s_router_ports_t router_port_o, 43 | // Additional outputs 44 | output full_o, 45 | output empty_o 46 | ); 47 | 48 | input_datapath u_input_datapath ( 49 | .clk (clk), 50 | .arst (arst), 51 | .fin_req_i (fin_req_i), 52 | .fin_resp_o (fin_resp_o), 53 | .fout_req_o (fout_req_o), 54 | .fout_resp_i(fout_resp_i), 55 | .full_o (full_o), 56 | .empty_o (empty_o) 57 | ); 58 | 59 | input_router#( 60 | .ROUTER_X_ID(ROUTER_X_ID), 61 | .ROUTER_Y_ID(ROUTER_Y_ID) 62 | ) u_input_router ( 63 | .clk (clk), 64 | .arst (arst), 65 | .flit_req_i (fout_req_o), 66 | .router_port_o(router_port_o) 67 | ); 68 | endmodule 69 | -------------------------------------------------------------------------------- /src/include/ravenoc_axi_fnc.svh: -------------------------------------------------------------------------------- 1 | `ifndef _RAVENOC_AXI_FNC_ 2 | `define _RAVENOC_AXI_FNC_ 3 | function automatic logic valid_op_size(axi_addr_t addr, axi_size_t asize); 4 | logic csr, buff, valid; 5 | 6 | csr = '0; 7 | buff = '0; 8 | valid = '0; 9 | 10 | for (int i=0;i 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module rr_arbiter #( 26 | parameter int N_OF_INPUTS = 2 27 | )( 28 | input clk, 29 | input arst, 30 | input update_i, 31 | input [N_OF_INPUTS-1:0] req_i, 32 | output logic [N_OF_INPUTS-1:0] grant_o 33 | ); 34 | logic [N_OF_INPUTS-1:0] mask_ff, next_mask; 35 | logic [N_OF_INPUTS-1:0] grant_ff, next_grant; 36 | 37 | always_comb begin 38 | next_mask = mask_ff; 39 | next_grant = grant_ff; 40 | grant_o = grant_ff; 41 | 42 | // We only check the inputs during the update == 1 43 | if (update_i) begin 44 | next_grant = '0; 45 | 46 | /*verilator coverage_off*/ 47 | // Checking each master against the mask 48 | for (int i=0; i 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 09.03.2021 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import random 10 | import cocotb 11 | import os 12 | import logging 13 | import pytest 14 | 15 | from common_noc.testbench import Tb 16 | from common_noc.constants import noc_const 17 | from common_noc.ravenoc_pkt import RaveNoC_pkt 18 | from cocotb_test.simulator import run 19 | from cocotb.regression import TestFactory 20 | from random import randrange 21 | from cocotb.result import TestFailure 22 | import itertools 23 | 24 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 25 | noc_flavor = os.getenv("FLAVOR") 26 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 27 | 28 | # Setup testbench 29 | idle = "no_idle" if idle_inserter == None else "w_idle" 30 | backp = "no_backpressure" if backpressure_inserter == None else "w_backpressure" 31 | tb = Tb(dut, f"sim_{config_clk}_{idle}_{backp}", noc_cfg) 32 | tb.set_idle_generator(idle_inserter) 33 | tb.set_backpressure_generator(backpressure_inserter) 34 | await tb.setup_clks(config_clk) 35 | await tb.arst(config_clk) 36 | 37 | pkt = RaveNoC_pkt(cfg=noc_cfg) 38 | write = cocotb.fork(tb.write_pkt(pkt)) 39 | await tb.wait_irq() 40 | resp = await tb.read_pkt(pkt) 41 | tb.check_pkt(resp.data,pkt.msg) 42 | 43 | def cycle_pause(): 44 | return itertools.cycle([1, 1, 1, 0]) 45 | 46 | if cocotb.SIM_NAME: 47 | factory = TestFactory(run_test) 48 | factory.add_option("config_clk", ["AXI_slwT_NoC", "NoC_slwT_AXI", "NoC_equal_AXI"]) 49 | factory.add_option("idle_inserter", [None, cycle_pause]) 50 | factory.add_option("backpressure_inserter", [None, cycle_pause]) 51 | factory.generate_tests() 52 | 53 | @pytest.mark.parametrize("flavor",noc_const.regression_setup) 54 | def test_ravenoc_basic(flavor): 55 | """ 56 | Basic test that sends a packet over the NoC and checks it 57 | 58 | Test ID: 1 59 | 60 | Description: 61 | The simplest test to send a pkt over the NoC and checks it by reading back the correspondent destination router. 62 | """ 63 | module = os.path.splitext(os.path.basename(__file__))[0] 64 | SIM_BUILD = os.path.join(noc_const.TESTS_DIR, 65 | f"../../run_dir/sim_build_{noc_const.SIMULATOR}_{module}_{flavor}") 66 | noc_const.EXTRA_ENV['SIM_BUILD'] = SIM_BUILD 67 | noc_const.EXTRA_ENV['FLAVOR'] = flavor 68 | 69 | extra_args_sim = noc_const._get_cfg_args(flavor) 70 | 71 | run( 72 | python_search=[noc_const.TESTS_DIR], 73 | includes=noc_const.INC_DIR, 74 | verilog_sources=noc_const.VERILOG_SOURCES, 75 | toplevel=noc_const.TOPLEVEL, 76 | module=module, 77 | sim_build=SIM_BUILD, 78 | extra_env=noc_const.EXTRA_ENV, 79 | extra_args=extra_args_sim 80 | ) 81 | -------------------------------------------------------------------------------- /tb/test_max_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : test_ravenoc_basic.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 09.03.2021 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import cocotb 10 | import os 11 | import logging 12 | import pytest 13 | import string 14 | import random 15 | 16 | from common_noc.testbench import Tb 17 | from common_noc.constants import noc_const 18 | from common_noc.ravenoc_pkt import RaveNoC_pkt 19 | from cocotb_test.simulator import run 20 | from cocotb.regression import TestFactory 21 | from random import randrange 22 | from cocotb.result import TestFailure 23 | import itertools 24 | 25 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 26 | noc_flavor = os.getenv("FLAVOR") 27 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 28 | 29 | # Setup testbench 30 | idle = "no_idle" if idle_inserter == None else "w_idle" 31 | backp = "no_backpressure" if backpressure_inserter == None else "w_backpressure" 32 | tb = Tb(dut, f"sim_{config_clk}_{idle}_{backp}", noc_cfg) 33 | tb.set_idle_generator(idle_inserter) 34 | tb.set_backpressure_generator(backpressure_inserter) 35 | await tb.setup_clks(config_clk) 36 | await tb.arst(config_clk) 37 | 38 | max_size = (noc_cfg['max_sz_pkt']-1)*(int(noc_cfg['flit_data_width']/8)) 39 | msg = tb._get_random_string(length=max_size) 40 | pkt = RaveNoC_pkt(cfg=noc_cfg, msg=msg) 41 | tb.log.info(f"src={pkt.src[0]} x={pkt.src[1]} y={pkt.src[2]}") 42 | tb.log.info(f"dest={pkt.dest[0]} x={pkt.dest[1]} y={pkt.dest[2]}") 43 | write = cocotb.fork(tb.write_pkt(pkt, timeout=noc_const.TIMEOUT_AXI_EXT)) 44 | await tb.wait_irq() 45 | resp = await tb.read_pkt(pkt, timeout=noc_const.TIMEOUT_AXI_EXT) 46 | tb.check_pkt(resp.data,pkt.msg) 47 | 48 | def cycle_pause(): 49 | return itertools.cycle([1, 1, 1, 0]) 50 | 51 | 52 | if cocotb.SIM_NAME: 53 | factory = TestFactory(run_test) 54 | factory.add_option("config_clk", ["AXI_slwT_NoC", "NoC_slwT_AXI", "NoC_equal_AXI"]) 55 | factory.add_option("idle_inserter", [None, cycle_pause]) 56 | factory.add_option("backpressure_inserter", [None, cycle_pause]) 57 | factory.generate_tests() 58 | 59 | @pytest.mark.parametrize("flavor",noc_const.regression_setup) 60 | def test_max_data(flavor): 61 | """ 62 | Test if the NoC is capable to transfer a pkt with the max. size 63 | 64 | Test ID: 3 65 | 66 | Description: This test exercise the maximum payload that each pkt in the NoC can transfer 67 | thus, in a 32-bit NoC this value is equal to 1KB and in a 64-bit NoC, this is equal to 2KB 68 | of data. It's expected that the received pkt will matching with the correspondent sent one. 69 | """ 70 | module = os.path.splitext(os.path.basename(__file__))[0] 71 | SIM_BUILD = os.path.join(noc_const.TESTS_DIR, 72 | f"../../run_dir/sim_build_{noc_const.SIMULATOR}_{module}_{flavor}") 73 | noc_const.EXTRA_ENV['SIM_BUILD'] = SIM_BUILD 74 | noc_const.EXTRA_ENV['FLAVOR'] = flavor 75 | 76 | extra_args_sim = noc_const._get_cfg_args(flavor) 77 | 78 | run( 79 | python_search=[noc_const.TESTS_DIR], 80 | includes=noc_const.INC_DIR, 81 | verilog_sources=noc_const.VERILOG_SOURCES, 82 | toplevel=noc_const.TOPLEVEL, 83 | module=module, 84 | sim_build=SIM_BUILD, 85 | extra_env=noc_const.EXTRA_ENV, 86 | extra_args=extra_args_sim 87 | ) 88 | -------------------------------------------------------------------------------- /tb/test_throughput.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : test_throughput.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 09.03.2021 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import random 10 | import cocotb 11 | import os 12 | import logging 13 | import pytest 14 | import random 15 | import string 16 | 17 | from common_noc.testbench import Tb 18 | from common_noc.constants import noc_const 19 | from common_noc.ravenoc_pkt import RaveNoC_pkt 20 | from cocotb_test.simulator import run 21 | from cocotb.regression import TestFactory 22 | from cocotb.utils import get_sim_time 23 | from random import randrange 24 | from cocotb.result import TestFailure 25 | import itertools 26 | 27 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 28 | noc_flavor = os.getenv("FLAVOR") 29 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 30 | 31 | # Setup testbench 32 | tb = Tb(dut, f"sim_{config_clk}", noc_cfg) 33 | await tb.setup_clks(config_clk) 34 | await tb.arst(config_clk) 35 | 36 | max_size = (noc_cfg['max_sz_pkt']-1)*(int(noc_cfg['flit_data_width']/8)) 37 | msg = tb._get_random_string(length=max_size) 38 | pkt = RaveNoC_pkt(cfg=noc_cfg, src_dest=(0,noc_cfg['max_nodes']-1), msg=msg) 39 | start_time = get_sim_time(units='ns') 40 | write = cocotb.fork(tb.write_pkt(pkt, timeout=noc_const.TIMEOUT_AXI_EXT)) 41 | await tb.wait_irq() 42 | resp = await tb.read_pkt(pkt, timeout=noc_const.TIMEOUT_AXI_EXT) 43 | end_time = get_sim_time(units='ns') 44 | delta_time_txn = end_time - start_time 45 | max_size += (int(noc_cfg['flit_data_width']/8)) # Adding header flit into account 46 | tb.log.info("Delta time = %d ns",delta_time_txn) 47 | tb.log.info("Bytes transferred = %d B",max_size) 48 | bw = [] 49 | bw.append(float(max_size/(1024**2))/float(delta_time_txn*10**-9)) 50 | bw.append(float(max_size/(1024**3))/float(delta_time_txn*10**-9)) 51 | tb.log.info("BW = %.2f MB/s (%.2f GB/s)",bw[0],bw[1]) 52 | tb.check_pkt(resp.data,pkt.msg) 53 | 54 | def cycle_pause(): 55 | return itertools.cycle([1, 1, 1, 0]) 56 | 57 | if cocotb.SIM_NAME: 58 | factory = TestFactory(run_test) 59 | factory.add_option("config_clk", ["AXI_slwT_NoC", "NoC_slwT_AXI", "NoC_equal_AXI"]) 60 | factory.generate_tests() 61 | 62 | @pytest.mark.parametrize("flavor",noc_const.regression_setup) 63 | def test_throughput(flavor): 64 | """ 65 | Test to compute the max throughput of the NoC 66 | 67 | Test ID: 4 68 | 69 | Description: 70 | In this test we send the maximum payload pkt through the NoC from the router 0 to the 71 | last router (longest datapath), once we receive the first flit in the destination router, 72 | we start reading it simultaneously, once both operations are over. We then compare the 73 | data to check the integrity and compute the total throughput of this pkt over the NoC. 74 | """ 75 | module = os.path.splitext(os.path.basename(__file__))[0] 76 | SIM_BUILD = os.path.join(noc_const.TESTS_DIR, 77 | f"../../run_dir/sim_build_{noc_const.SIMULATOR}_{module}_{flavor}") 78 | noc_const.EXTRA_ENV['SIM_BUILD'] = SIM_BUILD 79 | noc_const.EXTRA_ENV['FLAVOR'] = flavor 80 | 81 | extra_args_sim = noc_const._get_cfg_args(flavor) 82 | 83 | run( 84 | python_search=[noc_const.TESTS_DIR], 85 | includes=noc_const.INC_DIR, 86 | verilog_sources=noc_const.VERILOG_SOURCES, 87 | toplevel=noc_const.TOPLEVEL, 88 | module=module, 89 | sim_build=SIM_BUILD, 90 | extra_env=noc_const.EXTRA_ENV, 91 | extra_args=extra_args_sim 92 | ) 93 | -------------------------------------------------------------------------------- /src/router/vc_buffer.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: vc_buffer.sv 3 | * Description: Virtual Channel Buffer, contains the flit fifo and the 4 | * handshake mech. to push out flit through the output in 5 | * terface. 6 | * Author: Anderson Ignacio da Silva 7 | * 8 | * MIT License 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | module vc_buffer 28 | import amba_axi_pkg::*; 29 | import ravenoc_pkg::*; 30 | ( 31 | input clk, 32 | input arst, 33 | input [VcWidth-1:0] vc_id_i, 34 | output logic [VcWidth-1:0] vc_id_o, 35 | // Input interface - from external input module 36 | input [FlitWidth-1:0] fdata_i, 37 | input valid_i, 38 | output logic ready_o, 39 | // Output Interface - to Router Ctrl 40 | output [FlitWidth-1:0] fdata_o, 41 | output logic valid_o, 42 | input ready_i, 43 | // Additional outputs 44 | output logic full_o, 45 | output logic empty_o 46 | ); 47 | logic write_flit; 48 | logic full, empty, error; 49 | logic read_flit; 50 | logic locked_by_route_ff; 51 | logic next_locked; 52 | s_flit_head_data_t flit; 53 | 54 | fifo#( 55 | .SLOTS(FlitBuff), 56 | .WIDTH(FlitWidth) 57 | ) u_virt_chn_fifo ( 58 | .clk (clk), 59 | .arst (arst), 60 | .write_i (write_flit), 61 | .read_i (read_flit), 62 | .data_i (fdata_i), 63 | .data_o (fdata_o), 64 | .error_o (error), 65 | .full_o (full), 66 | .empty_o (empty), 67 | .ocup_o () 68 | ); 69 | 70 | always_comb begin 71 | next_locked = locked_by_route_ff; 72 | flit = fdata_i; 73 | if (valid_i && (flit.type_f == HEAD_FLIT) && (flit.pkt_size != 'h0)) begin 74 | next_locked = ready_o; 75 | end 76 | else if (valid_i && flit.type_f == TAIL_FLIT) begin 77 | next_locked = ~ready_o; 78 | end 79 | end 80 | 81 | always_comb begin 82 | write_flit = ~full && valid_i && (flit.type_f == HEAD_FLIT ? ~locked_by_route_ff : '1); 83 | ready_o = ~full && (flit.type_f == HEAD_FLIT ? ~locked_by_route_ff : '1); 84 | valid_o = ~empty; 85 | read_flit = valid_o && ready_i; 86 | vc_id_o = vc_id_i; 87 | empty_o = empty; 88 | full_o = full; 89 | end 90 | 91 | always_ff @ (posedge clk or posedge arst) begin 92 | if (arst) begin 93 | locked_by_route_ff <= '0; 94 | end 95 | else begin 96 | locked_by_route_ff <= next_locked; 97 | end 98 | end 99 | 100 | `ifndef NO_ASSERTIONS 101 | illegal_vcd_ctrl_behaviour : assert property ( 102 | @(posedge clk) disable iff (arst) 103 | error == 'h0 104 | ) else $error("Illegal Virtual channel value behaviour - Error on FIFO!"); 105 | `endif 106 | endmodule 107 | -------------------------------------------------------------------------------- /src/router/fifo.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: fifo.sv 3 | * Description: Simple FIFO module 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module fifo # ( 26 | parameter int SLOTS = 2, 27 | parameter int WIDTH = 8 28 | )( 29 | input clk, 30 | input arst, 31 | input write_i, 32 | input read_i, 33 | input [WIDTH-1:0] data_i, 34 | output logic [WIDTH-1:0] data_o, 35 | output logic error_o, 36 | output logic full_o, 37 | output logic empty_o, 38 | output logic [$clog2(SLOTS>1?SLOTS:2):0] ocup_o 39 | ); 40 | `define MSB_SLOT $clog2(SLOTS>1?SLOTS:2) 41 | 42 | logic [SLOTS-1:0] [WIDTH-1:0] fifo_ff; 43 | logic [`MSB_SLOT:0] write_ptr_ff; 44 | logic [`MSB_SLOT:0] read_ptr_ff; 45 | logic [`MSB_SLOT:0] next_write_ptr; 46 | logic [`MSB_SLOT:0] next_read_ptr; 47 | logic [`MSB_SLOT:0] fifo_ocup; 48 | 49 | always_comb begin 50 | next_read_ptr = read_ptr_ff; 51 | next_write_ptr = write_ptr_ff; 52 | if (SLOTS == 1) begin 53 | empty_o = (write_ptr_ff == read_ptr_ff); 54 | full_o = (write_ptr_ff[0] != read_ptr_ff[0]); 55 | data_o = empty_o ? '0 : fifo_ff[0]; 56 | end 57 | else begin 58 | empty_o = (write_ptr_ff == read_ptr_ff); 59 | full_o = (write_ptr_ff[`MSB_SLOT-1:0] == read_ptr_ff[`MSB_SLOT-1:0]) && 60 | (write_ptr_ff[`MSB_SLOT] != read_ptr_ff[`MSB_SLOT]); 61 | data_o = empty_o ? '0 : fifo_ff[read_ptr_ff[`MSB_SLOT-1:0]]; 62 | end 63 | 64 | if (write_i && ~full_o) 65 | next_write_ptr = write_ptr_ff + 'd1; 66 | 67 | if (read_i && ~empty_o) 68 | next_read_ptr = read_ptr_ff + 'd1; 69 | 70 | error_o = (write_i && full_o) || (read_i && empty_o); 71 | fifo_ocup = write_ptr_ff - read_ptr_ff; 72 | ocup_o = fifo_ocup; 73 | end 74 | 75 | always_ff @ (posedge clk or posedge arst) begin 76 | if (arst) begin 77 | write_ptr_ff <= '0; 78 | read_ptr_ff <= '0; 79 | fifo_ff <= '0; 80 | end 81 | else begin 82 | write_ptr_ff <= next_write_ptr; 83 | read_ptr_ff <= next_read_ptr; 84 | if (write_i && ~full_o) 85 | if (SLOTS == 1) begin 86 | fifo_ff[0] <= data_i; 87 | end 88 | else begin 89 | fifo_ff[write_ptr_ff[`MSB_SLOT-1:0]] <= data_i; 90 | end 91 | end 92 | end 93 | 94 | `ifndef NO_ASSERTIONS 95 | initial begin 96 | illegal_fifo_slot : assert (2**$clog2(SLOTS) == SLOTS) 97 | else $error("FIFO Slots must be power of 2"); 98 | 99 | min_fifo_size : assert (SLOTS >= 1) 100 | else $error("FIFO size of SLOTS defined is illegal!"); 101 | end 102 | `endif 103 | 104 | endmodule 105 | -------------------------------------------------------------------------------- /src/include/ravenoc_defines.svh: -------------------------------------------------------------------------------- 1 | `ifndef _RAVENOC_DEFINES_ 2 | `define _RAVENOC_DEFINES_ 3 | 4 | `ifndef FLIT_DATA_WIDTH 5 | `define FLIT_DATA_WIDTH 32 // Flit width data in bits 6 | `endif 7 | 8 | `ifndef FLIT_TP_WIDTH 9 | `define FLIT_TP_WIDTH 2 // Flit Width type 10 | `endif 11 | 12 | `ifndef FLIT_BUFF 13 | `define FLIT_BUFF 2 // Number of flits buffered in the virtual 14 | `endif //channel fifo, MUST BE POWER OF 2 1..2..4..8 15 | 16 | `ifndef N_VIRT_CHN 17 | `define N_VIRT_CHN 3 // Number of virtual channels 18 | `endif 19 | 20 | `ifndef H_PRIORITY 21 | `define H_PRIORITY ZeroHighPrior // Priority asc/descending on Virtual 22 | `endif // channel 23 | 24 | `ifndef NOC_CFG_SZ_ROWS 25 | `define NOC_CFG_SZ_ROWS 2 // NoC size rows 26 | `endif 27 | 28 | `ifndef NOC_CFG_SZ_COLS 29 | `define NOC_CFG_SZ_COLS 2 // NoC size cols 30 | `endif 31 | 32 | `ifndef ROUTING_ALG 33 | `define ROUTING_ALG XYAlg // Routing algorithm 34 | `endif 35 | 36 | `ifndef MAX_SZ_PKT 37 | `define MAX_SZ_PKT 256 // Max number of flits per packet 38 | `endif 39 | 40 | `ifndef N_CSR_REGS 41 | `define N_CSR_REGS 8 // Total number of CSR regs 42 | `endif 43 | 44 | `ifndef AUTO_ADD_PKT_SZ 45 | `define AUTO_ADD_PKT_SZ 0 // If 1, it'll overwrite the pkt size on the flit gen 46 | `endif 47 | 48 | `define MIN_CLOG(X) (X>1?X:2) 49 | 50 | //********************* 51 | // 52 | // AXI Definitions 53 | // 54 | // ******************** 55 | `ifndef AXI_ADDR_WIDTH 56 | `define AXI_ADDR_WIDTH 32 57 | `endif 58 | 59 | `ifndef AXI_DATA_WIDTH 60 | `define AXI_DATA_WIDTH `FLIT_DATA_WIDTH 61 | `endif 62 | 63 | `ifndef AXI_ALEN_WIDTH 64 | `define AXI_ALEN_WIDTH 8 65 | `endif 66 | 67 | `ifndef AXI_ASIZE_WIDTH 68 | `define AXI_ASIZE_WIDTH 3 69 | `endif 70 | 71 | `ifndef AXI_MAX_OUTSTD_RD 72 | `define AXI_MAX_OUTSTD_RD 2 73 | `endif 74 | 75 | `ifndef AXI_MAX_OUTSTD_WR 76 | `define AXI_MAX_OUTSTD_WR 2 77 | `endif 78 | 79 | `ifndef AXI_USER_RESP_WIDTH 80 | `define AXI_USER_RESP_WIDTH 2 81 | `endif 82 | // Not used these signals in the logic for now 83 | `ifndef AXI_USER_REQ_WIDTH 84 | `define AXI_USER_REQ_WIDTH 2 85 | `endif 86 | 87 | `ifndef AXI_USER_DATA_WIDTH 88 | `define AXI_USER_DATA_WIDTH 2 89 | `endif 90 | 91 | `ifndef AXI_TXN_ID_WIDTH 92 | `define AXI_TXN_ID_WIDTH 1 93 | `endif 94 | 95 | // Number of flits that each read buffer 96 | // in the AXI slave can hold it (per VC) 97 | `ifndef RD_AXI_BFF 98 | `define RD_AXI_BFF(x) x<=2?(1< "} 12 | set tcl_prompt2 {puts -nonewline "> "} 13 | set vlog_format %h 14 | set vhdl_format %v 15 | set real_precision 6 16 | set display_unit auto 17 | set time_unit module 18 | set heap_garbage_size -200 19 | set heap_garbage_time 0 20 | set assert_report_level note 21 | set assert_stop_level error 22 | set autoscope yes 23 | set assert_1164_warnings yes 24 | set pack_assert_off {} 25 | set severity_pack_assert_off {note warning} 26 | set assert_output_stop_level failed 27 | set tcl_debug_level 0 28 | set relax_path_name 1 29 | set vhdl_vcdmap XX01ZX01X 30 | set intovf_severity_level ERROR 31 | set probe_screen_format 0 32 | set rangecnst_severity_level ERROR 33 | set textio_severity_level ERROR 34 | set vital_timing_checks_on 1 35 | set vlog_code_show_force 0 36 | set assert_count_attempts 1 37 | set tcl_all64 false 38 | set tcl_runerror_exit false 39 | set assert_report_incompletes 0 40 | set show_force 1 41 | set force_reset_by_reinvoke 0 42 | set tcl_relaxed_literal 0 43 | set probe_exclude_patterns {} 44 | set probe_packed_limit 4k 45 | set probe_unpacked_limit 16k 46 | set assert_internal_msg no 47 | set svseed 1 48 | set assert_reporting_mode 0 49 | alias . run 50 | alias quit exit 51 | database -open -shm -into xcelium.shm xcelium.shm -default 52 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 53 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 54 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 55 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 56 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 57 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 58 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 59 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 60 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 61 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 62 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 63 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 64 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 65 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 66 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 67 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 68 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 69 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 70 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 71 | probe -create -database xcelium.shm tb_top -all -variables -generics -dynamic -depth all -tasks -functions -uvm 72 | 73 | simvision -input restore.tcl.svcf 74 | -------------------------------------------------------------------------------- /src/ni/pkt_proc.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: pkt_proc.sv 3 | * Description: Implements packet processor combo logic to 4 | * to AXI slave and to the NoC 5 | * Author: Anderson Ignacio da Silva 6 | * 7 | * MIT License 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | module pkt_proc 27 | import amba_axi_pkg::*; 28 | import ravenoc_pkg::*; 29 | ( 30 | input clk_axi, 31 | input arst_axi, 32 | 33 | // Interface with NoC 34 | router_if.send_flit local_send, 35 | router_if.recv_flit local_recv, 36 | 37 | // Interface with AXI Slave 38 | // AXI Slave -> Pkt Gen 39 | input s_pkt_out_req_t pkt_out_req_i, 40 | output s_pkt_out_resp_t pkt_out_resp_o, 41 | 42 | // AXI Salve <- Pkt Gen 43 | output s_pkt_in_req_t pkt_in_req_o, 44 | input s_pkt_in_resp_t pkt_in_resp_i 45 | ); 46 | logic [PktWidth-1:0] pkt_cnt_ff, next_pkt_cnt; 47 | logic wr_txn_ff, next_wr_txn; 48 | 49 | // ************************** 50 | // 51 | // Send flits from AXI Wr data channel -> NoC (local input buffer) 52 | // 53 | // ************************** 54 | always_comb begin : to_noc 55 | local_send.req = '0; 56 | pkt_out_resp_o.ready = local_send.resp.ready; 57 | next_wr_txn = wr_txn_ff; 58 | next_pkt_cnt = pkt_cnt_ff; 59 | 60 | if (pkt_out_req_i.valid) begin 61 | local_send.req.fdata[FlitDataWidth-1:0] = pkt_out_req_i.flit_data_width; 62 | local_send.req.vc_id = pkt_out_req_i.vc_id; 63 | local_send.req.valid = 1'b1; 64 | 65 | if (~wr_txn_ff && (pkt_out_req_i.pkt_sz > 0)) begin 66 | next_wr_txn = pkt_out_resp_o.ready; 67 | next_pkt_cnt = (next_wr_txn) ? (pkt_out_req_i.pkt_sz-'d1) : 'd0; 68 | end 69 | 70 | if (wr_txn_ff && (pkt_cnt_ff > 0) && pkt_out_resp_o.ready) begin 71 | next_pkt_cnt = pkt_cnt_ff - 'd1; 72 | end 73 | 74 | if (wr_txn_ff && (pkt_cnt_ff == 0) && pkt_out_resp_o.ready) begin 75 | next_wr_txn = 1'b0; 76 | end 77 | 78 | if (~wr_txn_ff) begin 79 | local_send.req.fdata[FlitWidth-1:FlitWidth-2] = HEAD_FLIT; 80 | end 81 | else if (wr_txn_ff && (pkt_cnt_ff > 0)) begin 82 | local_send.req.fdata[FlitWidth-1:FlitWidth-2] = BODY_FLIT; 83 | end 84 | else begin 85 | local_send.req.fdata[FlitWidth-1:FlitWidth-2] = TAIL_FLIT; 86 | end 87 | end 88 | end 89 | 90 | always_ff @ (posedge clk_axi or posedge arst_axi) begin 91 | if (arst_axi) begin 92 | pkt_cnt_ff <= 'd0; 93 | wr_txn_ff <= 1'b0; 94 | end 95 | else begin 96 | pkt_cnt_ff <= next_pkt_cnt; 97 | wr_txn_ff <= next_wr_txn; 98 | end 99 | end 100 | 101 | // ************************** 102 | // 103 | // Receive flits from NoC -> Send to AXI RX buffer 104 | // 105 | // ************************** 106 | always_comb begin : from_noc 107 | pkt_in_req_o.valid = local_recv.req.valid; 108 | // We remove the flit type to send to the buffer 109 | pkt_in_req_o.flit_data_width = local_recv.req.fdata[FlitDataWidth-1:0]; 110 | pkt_in_req_o.flit_raw = local_recv.req.fdata; 111 | pkt_in_req_o.rq_vc = local_recv.req.vc_id; 112 | pkt_in_req_o.f_type = flit_type_t'(local_recv.req.fdata[FlitDataWidth+:2]); 113 | local_recv.resp.ready = pkt_in_resp_i.ready; 114 | end 115 | endmodule 116 | -------------------------------------------------------------------------------- /tb/test_virt_chn_qos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : test_virt_chn_qos.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 29.03.2021 7 | # Last Modified Date: 29.03.2021 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import random 10 | import cocotb 11 | import os 12 | import logging 13 | import pytest 14 | import random 15 | import string 16 | 17 | from common_noc.testbench import Tb 18 | from common_noc.constants import noc_const 19 | from common_noc.ravenoc_pkt import RaveNoC_pkt 20 | from cocotb_test.simulator import run 21 | from cocotb.regression import TestFactory 22 | from cocotb.triggers import ClockCycles, RisingEdge, with_timeout, ReadOnly, Event 23 | from cocotb.utils import get_sim_time 24 | from random import randrange 25 | from cocotb.result import TestFailure 26 | import itertools 27 | 28 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 29 | noc_flavor = os.getenv("FLAVOR") 30 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 31 | 32 | # Setup testbench 33 | tb = Tb(dut,f"sim_{config_clk}", noc_cfg) 34 | await tb.setup_clks(config_clk) 35 | await tb.arst(config_clk) 36 | 37 | if (noc_cfg['max_nodes'] >= 3) and (noc_cfg['n_virt_chn']>1): 38 | max_size = (noc_cfg['max_sz_pkt']-1)*(int(noc_cfg['flit_data_width']/8)) 39 | 40 | high_prior_vc = (noc_cfg['n_virt_chn']-1) if noc_cfg['h_priority'] == "ZeroLowPrior" else 0 41 | low_prior_vc = 0 if noc_cfg['h_priority'] == "ZeroLowPrior" else (noc_cfg['n_virt_chn']-1) 42 | 43 | pkt_lp = RaveNoC_pkt(cfg=noc_cfg, msg=tb._get_random_string(max_size), src_dest=(1,noc_cfg['max_nodes']-1), virt_chn_id=low_prior_vc) 44 | pkt_hp = RaveNoC_pkt(cfg=noc_cfg, src_dest=(0,noc_cfg['max_nodes']-1), virt_chn_id=high_prior_vc) 45 | pkts = [pkt_hp,pkt_lp] 46 | 47 | #We need to extend the timeout once it's the max pkt sz 48 | wr_lp_pkt = cocotb.fork(tb.write_pkt(pkt=pkt_lp, timeout=noc_const.TIMEOUT_AXI_EXT)) 49 | wr_hp_pkt = cocotb.fork(tb.write_pkt(pkt=pkt_hp,use_side_if=1)) 50 | 51 | # HP pkt should finish first 52 | await wr_hp_pkt 53 | 54 | # Just to ensure the HP pkt has been sent over the NoC 55 | # and the LP pkt is still being processed 56 | assert((wr_lp_pkt._finished == False) and (wr_hp_pkt._finished == True)) 57 | lp_prior = 2**(noc_cfg['n_virt_chn']-1) 58 | hp_prior = 2**(0) 59 | irq_lp_hp = (lp_prior+hp_prior) << (noc_cfg['max_nodes']-1)*noc_cfg['n_virt_chn'] 60 | tb.log.info("IRQs to wait: %d",irq_lp_hp) 61 | await tb.wait_irq_x(irq_lp_hp) 62 | 63 | for pkt in pkts: 64 | resp = await tb.read_pkt(pkt=pkt, timeout=noc_const.TIMEOUT_AXI_EXT) 65 | tb.check_pkt(resp.data,pkt.msg) 66 | else: 67 | tb.log.info("Test not executed due to the NoC cfg, min >=3 routers && min > 1 VCs") 68 | 69 | def cycle_pause(): 70 | return itertools.cycle([1, 1, 1, 0]) 71 | 72 | if cocotb.SIM_NAME: 73 | factory = TestFactory(run_test) 74 | factory.add_option("config_clk", ["AXI_slwT_NoC", "NoC_slwT_AXI", "NoC_equal_AXI"]) 75 | factory.generate_tests() 76 | 77 | @pytest.mark.parametrize("flavor",noc_const.regression_setup) 78 | def test_virt_chn_qos(flavor): 79 | """ 80 | Test the QoS of VCs in the NoC 81 | 82 | Test ID: 6 83 | 84 | Description: 85 | In this test, we send a LOW priority (can be vc_id=0 or vc_id=Max, depending upon cfg) pkt with the maximum size of 86 | payload and a HIGH priority pkt with a single flit size through the NoC. The src routers which it'll be send will be 87 | router 0 for the HP pkt and router 1 for the LP one, because it'll be executed at the same time with fork(). Both pkts 88 | will have as destiny the last Router in NoC thus sharing the same datapath. The expectation is that the HP pkt will 89 | finish earlier than the LP (assert wr_....) using the same datapath, then we read both and check if the respective 90 | contents are matching. 91 | """ 92 | module = os.path.splitext(os.path.basename(__file__))[0] 93 | SIM_BUILD = os.path.join(noc_const.TESTS_DIR, 94 | f"../../run_dir/sim_build_{noc_const.SIMULATOR}_{module}_{flavor}") 95 | noc_const.EXTRA_ENV['SIM_BUILD'] = SIM_BUILD 96 | noc_const.EXTRA_ENV['FLAVOR'] = flavor 97 | 98 | extra_args_sim = noc_const._get_cfg_args(flavor) 99 | 100 | run( 101 | python_search=[noc_const.TESTS_DIR], 102 | includes=noc_const.INC_DIR, 103 | verilog_sources=noc_const.VERILOG_SOURCES, 104 | toplevel=noc_const.TOPLEVEL, 105 | module=module, 106 | sim_build=SIM_BUILD, 107 | extra_env=noc_const.EXTRA_ENV, 108 | extra_args=extra_args_sim 109 | ) 110 | -------------------------------------------------------------------------------- /tb/test_all_buffers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File : test_all_buffers.py 3 | # License : MIT license 4 | # Author : Anderson Ignacio da Silva (aignacio) 5 | # Date : 09.03.2021 6 | # Last Modified Date: 31.03.2021 7 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 8 | import random 9 | import cocotb 10 | import os 11 | import logging 12 | import pytest 13 | 14 | from common_noc.testbench import Tb 15 | from common_noc.constants import noc_const 16 | from common_noc.ravenoc_pkt import RaveNoC_pkt 17 | from cocotb_test.simulator import run 18 | from cocotb.regression import TestFactory 19 | import itertools 20 | 21 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 22 | noc_flavor = os.getenv("FLAVOR") 23 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 24 | 25 | # Setup testbench 26 | idle = "no_idle" if idle_inserter == None else "w_idle" 27 | backp = "no_backpressure" if backpressure_inserter == None else "w_backpressure" 28 | tb = Tb(dut, f"sim_{config_clk}_{idle}_{backp}", noc_cfg) 29 | tb.set_idle_generator(idle_inserter) 30 | tb.set_backpressure_generator(backpressure_inserter) 31 | await tb.setup_clks(config_clk) 32 | await tb.arst(config_clk) 33 | 34 | # Populate all buffers of all vcs of all routers from router 0 35 | pkts = [] 36 | for router in range(1,noc_cfg['max_nodes']): 37 | for vc in range(0,noc_cfg['n_virt_chn']): 38 | for flit_buff in range(0,buff_rd_vc(vc)): # We follow this Equation to discover how many buffs exists in each router / (RD_AXI_BFF(x) x<=2?(1< 8 | * 9 | * MIT License 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | module input_datapath 29 | import amba_axi_pkg::*; 30 | import ravenoc_pkg::*; 31 | ( 32 | input clk, 33 | input arst, 34 | // Input interface - from external input module 35 | input s_flit_req_t fin_req_i, 36 | output s_flit_resp_t fin_resp_o, 37 | // Output Interface - Output module 38 | output s_flit_req_t fout_req_o, 39 | input s_flit_resp_t fout_resp_i, 40 | // Additional outputs 41 | output full_o, 42 | output empty_o 43 | ); 44 | s_flit_req_t [NumVirtChn-1:0] from_input_req; 45 | s_flit_resp_t [NumVirtChn-1:0] from_input_resp; 46 | 47 | s_flit_req_t [NumVirtChn-1:0] to_output_req; 48 | s_flit_resp_t [NumVirtChn-1:0] to_output_resp; 49 | 50 | logic [VcWidth-1:0] vc_ch_act_in; 51 | logic req_in; 52 | 53 | logic [VcWidth-1:0] vc_ch_act_out; 54 | logic req_out; 55 | 56 | logic [NumVirtChn-1:0] full_vc; 57 | logic [NumVirtChn-1:0] empty_vc; 58 | 59 | for(genvar vc_id=0;vc_id=0;i--) begin 87 | from_input_req[i[VcWidth-1:0]].fdata = fin_req_i.fdata; 88 | 89 | if (fin_req_i.vc_id == i[VcWidth-1:0] && fin_req_i.valid && ~req_in) begin 90 | vc_ch_act_in = i[VcWidth-1:0]; 91 | req_in = 1; 92 | end 93 | end 94 | 95 | full_o = 1'b0; 96 | empty_o = 1'b0; 97 | 98 | if (req_in) begin 99 | from_input_req[vc_ch_act_in].valid = fin_req_i.valid; 100 | from_input_req[vc_ch_act_in].vc_id = vc_ch_act_in; 101 | fin_resp_o.ready = from_input_resp[vc_ch_act_in].ready; 102 | full_o = full_vc[vc_ch_act_in]; 103 | empty_o = empty_vc[vc_ch_act_in]; 104 | end 105 | else begin 106 | fin_resp_o.ready = '1; 107 | end 108 | end 109 | 110 | // Output mux 111 | always_comb begin : router_mux 112 | fout_req_o = '0; 113 | vc_ch_act_out = '0; 114 | req_out = '0; 115 | to_output_resp = '0; 116 | 117 | if (HighPriority == ZeroLowPrior) begin 118 | for (int i=NumVirtChn-1;i>=0;i--) 119 | if (to_output_req[i].valid) begin 120 | vc_ch_act_out = i[VcWidth-1:0]; 121 | req_out = 1; 122 | break; 123 | end 124 | end 125 | else begin 126 | for (int i=0;i 9 | * 10 | * MIT License 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in all 19 | * copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | */ 29 | module input_router 30 | import amba_axi_pkg::*; 31 | import ravenoc_pkg::*; 32 | #( 33 | parameter logic [XWidth-1:0] ROUTER_X_ID = 0, 34 | parameter logic [YWidth-1:0] ROUTER_Y_ID = 0 35 | )( 36 | input clk, 37 | input arst, 38 | input s_flit_req_t flit_req_i, 39 | output s_router_ports_t router_port_o 40 | ); 41 | logic [NumVirtChn-1:0] [2:0] routing_table_ff; 42 | routes_t next_rt; 43 | s_flit_head_data_t flit; 44 | logic new_rt; 45 | 46 | always_comb begin : routing_process 47 | next_rt = NORTH_PORT; 48 | flit = flit_req_i.fdata; 49 | new_rt = (flit_req_i.valid && flit.type_f == HEAD_FLIT); 50 | 51 | /* verilator lint_off UNSIGNED */ 52 | /* verilator lint_off CMPCONST */ 53 | if (new_rt) begin 54 | if (RoutingAlg == XYAlg) begin 55 | if (flit.x_dest == ROUTER_X_ID && 56 | flit.y_dest == ROUTER_Y_ID) begin : flit_arrived_x 57 | next_rt = LOCAL_PORT; 58 | end 59 | else if (flit.x_dest == ROUTER_X_ID) begin : adjust_y_then 60 | if (flit.y_dest < ROUTER_Y_ID) begin 61 | next_rt = WEST_PORT; 62 | end 63 | else begin 64 | next_rt = EAST_PORT; 65 | end 66 | end 67 | else begin : adjust_x_first 68 | if (flit.x_dest > ROUTER_X_ID) begin 69 | next_rt = SOUTH_PORT; 70 | end 71 | else begin 72 | next_rt = NORTH_PORT; 73 | end 74 | end 75 | end 76 | else if (RoutingAlg == YXAlg) begin 77 | if (flit.x_dest == ROUTER_X_ID && 78 | flit.y_dest == ROUTER_Y_ID) begin : flit_arrived_y 79 | next_rt = LOCAL_PORT; 80 | end 81 | else if (flit.y_dest == ROUTER_Y_ID) begin : adjust_x_then 82 | if (flit.x_dest < ROUTER_X_ID) begin 83 | next_rt = NORTH_PORT; 84 | end 85 | else begin 86 | next_rt = SOUTH_PORT; 87 | end 88 | end 89 | else begin : adjust_y_first 90 | if (flit.y_dest > ROUTER_Y_ID) begin 91 | next_rt = EAST_PORT; 92 | end 93 | else begin 94 | next_rt = WEST_PORT; 95 | end 96 | end 97 | end 98 | end 99 | /* verilator lint_on UNSIGNED */ 100 | /* verilator lint_on CMPCONST */ 101 | end 102 | 103 | always_comb begin : router_mapping_control 104 | router_port_o = '0; 105 | 106 | if (new_rt) begin 107 | unique case(next_rt) 108 | NORTH_PORT: router_port_o.north_req = '1; 109 | SOUTH_PORT: router_port_o.south_req = '1; 110 | WEST_PORT: router_port_o.west_req = '1; 111 | EAST_PORT: router_port_o.east_req = '1; 112 | LOCAL_PORT: router_port_o.local_req = '1; 113 | default: router_port_o = '0; 114 | endcase 115 | end 116 | else if (flit_req_i.valid) begin 117 | unique case(routing_table_ff[flit_req_i.vc_id]) 118 | NORTH_PORT: router_port_o.north_req = '1; 119 | SOUTH_PORT: router_port_o.south_req = '1; 120 | WEST_PORT: router_port_o.west_req = '1; 121 | EAST_PORT: router_port_o.east_req = '1; 122 | LOCAL_PORT: router_port_o.local_req = '1; 123 | default: router_port_o = '0; 124 | endcase 125 | end 126 | end 127 | 128 | always_ff @ (posedge clk or posedge arst) begin 129 | if (arst) begin 130 | routing_table_ff <= '0; 131 | end 132 | else begin 133 | if (new_rt) begin 134 | routing_table_ff[flit_req_i.vc_id] <= next_rt; 135 | end 136 | end 137 | end 138 | 139 | `ifndef NO_ASSERTIONS 140 | //router_not_one_hot : assert property ( 141 | //@(posedge clk) disable iff (arst) 142 | //$onehot(router_port_o) 143 | //) else $error("Input Router is not one hot type!"); 144 | `endif 145 | endmodule 146 | -------------------------------------------------------------------------------- /src/ni/cdc_pkt.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: cdc_pkt.sv 3 | * Description: It encapsulates a pkt into different clk domains 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module cdc_pkt 26 | import amba_axi_pkg::*; 27 | import ravenoc_pkg::*; 28 | #( 29 | parameter int CDC_TAPS = 2 30 | )( 31 | input clk_axi, 32 | input clk_noc, 33 | 34 | input arst_axi, 35 | input arst_noc, 36 | input bypass_cdc_i, 37 | // AXI Slave (pkt gen) -> NoC 38 | router_if.recv_flit flit_req_axi_axi, 39 | router_if.send_flit flit_req_axi_noc, 40 | // AXI Slave (pkt_gen) <- NoC 41 | router_if.recv_flit flit_req_noc_noc, 42 | router_if.send_flit flit_req_noc_axi 43 | ); 44 | localparam int WidthAxiToNoC = $bits(s_flit_req_t); 45 | 46 | logic [WidthAxiToNoC-1:0] input_afifo_axi_noc; 47 | logic [WidthAxiToNoC-1:0] output_afifo_axi_noc; 48 | logic wr_full_axi_noc; 49 | logic rd_empty_axi_noc; 50 | logic rd_enable_axi_noc; 51 | 52 | localparam int WidthNoCToAxi = $bits(s_flit_req_t); 53 | 54 | logic [WidthNoCToAxi-1:0] input_afifo_noc_axi; 55 | logic [WidthNoCToAxi-1:0] output_afifo_noc_axi; 56 | logic wr_full_noc_axi; 57 | logic rd_empty_noc_axi; 58 | logic rd_enable_noc_axi; 59 | 60 | //------------------------------------ 61 | // 62 | // AXI to NoC - CDC AFIFO Sync 63 | // Let's bring pkt requests to NoC domain, 64 | // Now it can go to the NoC bc it's coming 65 | // from the pkt generator combo logic 66 | // 67 | //------------------------------------ 68 | always_comb begin : axi_to_noc_flow 69 | if (bypass_cdc_i == 0) begin 70 | input_afifo_axi_noc = WidthAxiToNoC'(flit_req_axi_axi.req); 71 | flit_req_axi_axi.resp.ready = ~wr_full_axi_noc; 72 | flit_req_axi_noc.req = rd_empty_axi_noc ? s_flit_req_t'('0) : output_afifo_axi_noc; 73 | rd_enable_axi_noc = flit_req_axi_noc.resp.ready && flit_req_axi_noc.req.valid; 74 | end 75 | else begin 76 | input_afifo_axi_noc = '0; 77 | rd_enable_axi_noc = '0; 78 | 79 | flit_req_axi_noc.req = flit_req_axi_axi.req; 80 | flit_req_axi_axi.resp = flit_req_axi_noc.resp; 81 | end 82 | end 83 | 84 | async_gp_fifo#( 85 | .SLOTS (CDC_TAPS), 86 | .WIDTH (WidthAxiToNoC) 87 | ) u_afifo_axi_to_noc ( 88 | // AXI 89 | .clk_wr (clk_axi), 90 | .arst_wr (arst_axi), 91 | .wr_en_i (flit_req_axi_axi.req.valid), 92 | .wr_data_i (input_afifo_axi_noc), 93 | .wr_full_o (wr_full_axi_noc), 94 | // NoC 95 | .clk_rd (clk_noc), 96 | .arst_rd (arst_noc), 97 | .rd_en_i (rd_enable_axi_noc), 98 | .rd_data_o (output_afifo_axi_noc), 99 | .rd_empty_o (rd_empty_axi_noc) 100 | ); 101 | 102 | //------------------------------------ 103 | // 104 | // NoC to AXI - CDC AFIFO Sync 105 | // Let's bring pkt requests to AXI domain, 106 | // Now it can go to the pkt gen bc it's coming 107 | // from the NoC output module 108 | // 109 | //------------------------------------ 110 | always_comb begin : noc_to_noc_flow 111 | if (bypass_cdc_i == 0) begin 112 | input_afifo_noc_axi = WidthNoCToAxi'(flit_req_noc_noc.req); 113 | flit_req_noc_noc.resp.ready = ~wr_full_noc_axi; 114 | flit_req_noc_axi.req = rd_empty_noc_axi ? s_flit_req_t'('0) : output_afifo_noc_axi; 115 | rd_enable_noc_axi = flit_req_noc_axi.resp.ready && flit_req_noc_axi.req.valid; 116 | end 117 | else begin 118 | input_afifo_noc_axi = '0; 119 | rd_enable_noc_axi = '0; 120 | 121 | flit_req_noc_axi.req = flit_req_noc_noc.req; 122 | flit_req_noc_noc.resp = flit_req_noc_axi.resp; 123 | end 124 | end 125 | 126 | async_gp_fifo#( 127 | .SLOTS (CDC_TAPS), 128 | .WIDTH (WidthNoCToAxi) 129 | ) u_afifo_noc_to_axi ( 130 | // NoC 131 | .clk_wr (clk_noc), 132 | .arst_wr (arst_noc), 133 | .wr_en_i (flit_req_noc_noc.req.valid), 134 | .wr_data_i (input_afifo_noc_axi), 135 | .wr_full_o (wr_full_noc_axi), 136 | // AXI 137 | .clk_rd (clk_axi), 138 | .arst_rd (arst_axi), 139 | .rd_en_i (rd_enable_noc_axi), 140 | .rd_data_o (output_afifo_noc_axi), 141 | .rd_empty_o (rd_empty_noc_axi) 142 | ); 143 | endmodule 144 | -------------------------------------------------------------------------------- /src/router/output_module.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: output_module.sv 3 | * Description: Output module to route flits from input 4 | * module to the outputs. 5 | * 6 | * Author: Anderson Ignacio da Silva 7 | * 8 | * MIT License 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | module output_module 28 | import amba_axi_pkg::*; 29 | import ravenoc_pkg::*; 30 | ( 31 | input clk, 32 | input arst, 33 | // From input modules 34 | input s_flit_req_t [3:0] fin_req_i, 35 | output s_flit_resp_t [3:0] fin_resp_o, 36 | // To external of router 37 | output s_flit_req_t fout_req_o, 38 | input s_flit_resp_t fout_resp_i 39 | ); 40 | logic [NumVirtChn-1:0][3:0] req; 41 | logic [NumVirtChn-1:0][3:0] grant_im; 42 | logic [VcWidth-1:0] vc_ch_act_out; 43 | logic req_out; 44 | logic [NumVirtChn-1:0] update; 45 | logic [NumVirtChn-1:0] lock_ff, next_lock; 46 | s_flit_head_data_t hflit; 47 | logic sflit_done; 48 | logic nflit_done; 49 | 50 | /* verilator lint_off WIDTH */ 51 | for(genvar vc_id=0; vc_id=0;i--) 119 | if (|grant_im[i] && lock_ff[i]) begin 120 | vc_ch_act_out = i; 121 | req_out = 1; 122 | break; 123 | end 124 | end 125 | else begin 126 | for (int i=0;i http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf 6 | * -> https://zipcpu.com/blog/2018/07/06/afifo.html* File: async_fifo.sv 7 | * Author: Anderson Ignacio da Silva 8 | * 9 | * MIT License 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | module async_gp_fifo # ( 29 | parameter int SLOTS = 2, 30 | parameter int WIDTH = 8 31 | ) ( 32 | // Clock domain 1 33 | input clk_wr, 34 | input arst_wr, 35 | input wr_en_i, 36 | input [WIDTH-1:0] wr_data_i, 37 | output logic wr_full_o, 38 | 39 | // Clock domain 2 40 | input clk_rd, 41 | input arst_rd, 42 | input rd_en_i, 43 | output logic [WIDTH-1:0] rd_data_o, 44 | output logic rd_empty_o 45 | ); 46 | `define IDX_PTR w_wr_bin_ptr_ff[$clog2(SLOTS)-1:0] // Valid index pointer 47 | 48 | // Naming convention 49 | // ptr - pointer 50 | // wr - write 51 | // rd - read 52 | // bin - binary 53 | // 54 | // Pointers convention used follows the below pattern: 55 | // -> CLKDOMAIN(w/r)_TYPE(wr/rd)_ENCODING(bin/gray)_ptr 56 | // Example: 57 | // w_wr_bin_ptr - Write binary pointer in write clock domain 58 | // r_wr_gray_ptr - Write gray pointer in read clock domain 59 | // META_... - Metastable transition FF 60 | typedef logic [$clog2(SLOTS):0] ptr_t; 61 | logic [SLOTS-1:0] [WIDTH-1:0] array_fifo_ff; 62 | 63 | ptr_t w_wr_bin_ptr_ff, next_w_wr_bin_ptr; 64 | ptr_t w_rd_gry_ptr_ff, w_rd_bin_ptr; // We only bring to wr domain 65 | // the rd gray encoded ptr once 66 | // we only use it to gen the full 67 | // flag, and crossing gray encoding 68 | // it's more stable than bin encoding 69 | 70 | ptr_t r_rd_bin_ptr_ff, next_r_rd_bin_ptr; 71 | ptr_t r_wr_gry_ptr_ff; // We only bring to rd domain 72 | // the wr gray encoded ptr once 73 | // we only use it to gen the empty 74 | // flag, and crossing gray encoding 75 | // it's more stable than bin encoding 76 | ptr_t META_w_rd_gry_ff; 77 | ptr_t META_r_wr_gry_ff; 78 | 79 | //************************ 80 | // Functions 81 | //************************ 82 | function automatic ptr_t bin_to_gray (ptr_t input_bin); 83 | ptr_t value; 84 | value = (input_bin >> 1) ^ input_bin; 85 | return value; 86 | endfunction 87 | 88 | function automatic ptr_t gray_to_bin (ptr_t input_gray); 89 | ptr_t value; 90 | value = input_gray; 91 | for (int i=$clog2(SLOTS);i>0;i--) 92 | value[i-1] = value[i]^value[i-1]; 93 | return value; 94 | endfunction 95 | 96 | //************************ 97 | // Write logic 98 | //************************ 99 | always_comb begin : wr_pointer 100 | next_w_wr_bin_ptr = w_wr_bin_ptr_ff; 101 | w_rd_bin_ptr = gray_to_bin(w_rd_gry_ptr_ff); 102 | 103 | wr_full_o = (w_wr_bin_ptr_ff[$clog2(SLOTS)] == ~w_rd_bin_ptr[$clog2(SLOTS)]) && 104 | (w_wr_bin_ptr_ff[$clog2(SLOTS)-1:0] == w_rd_bin_ptr[$clog2(SLOTS)-1:0]); 105 | 106 | if (wr_en_i && ~wr_full_o) begin 107 | next_w_wr_bin_ptr = w_wr_bin_ptr_ff + 'd1; 108 | end 109 | end 110 | 111 | always_ff @ (posedge clk_wr or posedge arst_wr) begin 112 | if (arst_wr) begin 113 | w_wr_bin_ptr_ff <= ptr_t'(0); 114 | META_w_rd_gry_ff <= ptr_t'(0); 115 | w_rd_gry_ptr_ff <= ptr_t'(0); 116 | //array_fifo_ff <= '0; // --> Let's make it "low power" 117 | end 118 | else begin 119 | w_wr_bin_ptr_ff <= next_w_wr_bin_ptr; 120 | // 2FF Synchronizer: 121 | // Bring RD ptr to WR domain 122 | META_w_rd_gry_ff <= bin_to_gray(r_rd_bin_ptr_ff); 123 | w_rd_gry_ptr_ff <= META_w_rd_gry_ff; 124 | 125 | if (wr_en_i && ~wr_full_o) begin 126 | array_fifo_ff[`IDX_PTR] <= wr_data_i; 127 | end 128 | end 129 | end 130 | 131 | //************************ 132 | // Read logic 133 | //************************ 134 | always_comb begin : rd_pointer 135 | next_r_rd_bin_ptr = r_rd_bin_ptr_ff; 136 | 137 | rd_empty_o = (bin_to_gray(r_rd_bin_ptr_ff) == r_wr_gry_ptr_ff); 138 | 139 | if (rd_en_i && ~rd_empty_o) begin 140 | next_r_rd_bin_ptr = r_rd_bin_ptr_ff + 'd1; 141 | end 142 | 143 | rd_data_o = array_fifo_ff[r_rd_bin_ptr_ff[$clog2(SLOTS)-1:0]]; 144 | end 145 | 146 | always_ff @ (posedge clk_rd or posedge arst_rd) begin 147 | if (arst_rd) begin 148 | r_rd_bin_ptr_ff <= ptr_t'(0); 149 | META_r_wr_gry_ff <= ptr_t'(0); 150 | r_wr_gry_ptr_ff <= ptr_t'(0); 151 | end 152 | else begin 153 | r_rd_bin_ptr_ff <= next_r_rd_bin_ptr; 154 | // 2FF Synchronizer: 155 | // Bring RD ptr to WR domain 156 | META_r_wr_gry_ff <= bin_to_gray(w_wr_bin_ptr_ff); 157 | r_wr_gry_ptr_ff <= META_r_wr_gry_ff; 158 | end 159 | end 160 | 161 | `ifndef NO_ASSERTIONS 162 | initial begin 163 | illegal_fifo_slot : assert (2**$clog2(SLOTS) == SLOTS) 164 | else $error("ASYNC FIFO Slots must be power of 2"); 165 | 166 | min_fifo_size_2 : assert (SLOTS >= 2) 167 | else $error("ASYNC FIFO size of SLOTS defined is illegal!"); 168 | end 169 | `endif 170 | endmodule 171 | -------------------------------------------------------------------------------- /src/ravenoc.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: ravenoc.sv 3 | * Description: RaveNoC top module 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module ravenoc 26 | import amba_axi_pkg::*; 27 | import ravenoc_pkg::*; 28 | #( 29 | parameter bit [NoCSize-1:0] AXI_CDC_REQ = '1 30 | ) ( 31 | input [NoCSize-1:0] clk_axi, 32 | input clk_noc, 33 | input [NoCSize-1:0] arst_axi, 34 | input arst_noc, 35 | // NI interfaces 36 | input s_axi_mosi_t [NoCSize-1:0] axi_mosi_if, 37 | output s_axi_miso_t [NoCSize-1:0] axi_miso_if, 38 | // IRQs 39 | output s_irq_ni_t [NoCSize-1:0] irqs, 40 | // Used only in tb to bypass cdc module 41 | input [NoCSize-1:0] bypass_cdc 42 | ); 43 | router_if ns_con [(NoCCfgSzRows+1)*NoCCfgSzCols] (); 44 | router_if sn_con [(NoCCfgSzRows+1)*NoCCfgSzCols] (); 45 | router_if we_con [NoCCfgSzRows*(NoCCfgSzCols+1)] (); 46 | router_if ew_con [NoCCfgSzRows*(NoCCfgSzCols+1)] (); 47 | 48 | for(genvar x=0;x 0) ? 1 : 0; // First row 117 | connected_ports.south_req = (x < (NoCCfgSzRows-1)) ? 1 : 0; // Last row 118 | connected_ports.west_req = (y > 0) ? 1 : 0; // First collumn 119 | connected_ports.east_req = (y < (NoCCfgSzCols-1)) ? 1 : 0; // Last collumn 120 | connected_ports.local_req = 0; 121 | return connected_ports; 122 | endfunction 123 | /*verilator coverage_on*/ 124 | `ifndef NO_ASSERTIONS 125 | initial begin 126 | illegal_noc_col_sz : assert (NoCCfgSzRows == 1 ? (NoCCfgSzCols >= 2) : 1) 127 | else $error("Invalid NoC PARAM: NoC Col (y) size should be >= 2 if Row == 1!"); 128 | 129 | illegal_noc_row_sz : assert (NoCCfgSzCols == 1 ? (NoCCfgSzRows >= 2) : 1) 130 | else $error("Invalid NoC PARAM: NoC RoW (x) size should be >= 2 if Col == 1!"); 131 | end 132 | `endif 133 | endmodule 134 | 135 | module ravenoc_dummy ( 136 | input local_port, 137 | router_if.send_flit send, 138 | router_if.recv_flit recv 139 | ); 140 | always_comb begin 141 | if (local_port == 0) begin 142 | recv.resp = '0; 143 | send.req = '0; 144 | end 145 | else begin 146 | recv.resp = '1; 147 | send.req = '0; 148 | end 149 | end 150 | endmodule 151 | 152 | //Check python NoC size - run.py 153 | //for x in range(noc_lines): 154 | //for y in range(noc_collumns): 155 | //if (y>0 and y<(noc_collumns-1)): 156 | //print("-R-",end=''); 157 | //elif (y == 0): 158 | //print("R-",end=''); 159 | //else: 160 | //print("-R"); 161 | //for x in range(noc_lines): 162 | //for y in range(noc_collumns): 163 | //if (y==0): 164 | //print("##########"); 165 | //index_val["NorthIdx"] = y+(x*noc_collumns); 166 | //index_val["SouthIdx"] = y+((x+1)*noc_collumns); 167 | //index_val["WestIdx"] = y+(x*(noc_collumns+1)); 168 | //index_val["EastIdx"] = (y+1)+(x*(noc_collumns+1)); 169 | //print("Router ("+str(x)+","+str(y)+") ---> "+str(index_val)); 170 | 171 | -------------------------------------------------------------------------------- /src/ni/axi_csr.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: axi_csr.sv 3 | * Description: All the NoC CSRs 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module axi_csr 26 | import amba_axi_pkg::*; 27 | import ravenoc_pkg::*; 28 | #( 29 | parameter logic [XWidth-1:0] ROUTER_X_ID = 0, 30 | parameter logic [YWidth-1:0] ROUTER_Y_ID = 0, 31 | parameter bit CDC_REQUIRED = 1 32 | ) ( 33 | input clk_axi, 34 | input arst_axi, 35 | // Custom I/F just to exchange data 36 | input s_csr_req_t csr_req_i, 37 | output s_csr_resp_t csr_resp_o, 38 | // Additional inputs 39 | input [NumVirtChn-1:0][FlitTpWidth-1:0] f_type_rd_buff_i, 40 | input [NumVirtChn-1:0] empty_rd_bff_i, 41 | input [NumVirtChn-1:0] full_rd_bff_i, 42 | input [NumVirtChn-1:0][15:0] fifo_ocup_rd_bff_i, 43 | input [NumVirtChn-1:0][PktWidth-1:0] pkt_size_vc_i, 44 | input full_wr_fifo_i, 45 | // Additional outputs 46 | output s_irq_ni_t irqs_out_o 47 | ); 48 | logic error_ff, error_rd, error_wr, next_error; 49 | logic [31:0] decoded_data; 50 | s_irq_ni_mux_t irq_mux; 51 | logic [31:0] mux_out_ff, next_mux_out; 52 | s_irq_ni_mux_t irq_mux_ff, next_irq_mux; 53 | logic [31:0] irq_mask_ff, next_irq_mask; 54 | logic ack_irq_ff, next_ack, reset_ack; 55 | 56 | always_comb begin : wireup_csr 57 | next_error = error_rd; 58 | csr_resp_o.ready = 1'b1; 59 | csr_resp_o.error = error_ff || error_wr; // We need to pull the write error 60 | // to register on b-chn 61 | csr_resp_o.data_out = mux_out_ff; 62 | end 63 | 64 | always_comb begin : csr_decoder_w 65 | error_wr = '0; 66 | next_irq_mux = irq_mux_ff; 67 | next_irq_mask = irq_mask_ff; 68 | reset_ack = 1'b0; 69 | 70 | if (csr_req_i.valid && csr_req_i.rd_or_wr) begin 71 | /* verilator lint_off WIDTH */ 72 | unique case(csr_req_i.addr-(`AXI_CSR_BASE_ADDR & 16'hFFFF)) 73 | RAVENOC_VERSION: error_wr = 'h1; 74 | ROUTER_ROW_X_ID: error_wr = 'h1; 75 | ROUTER_COL_Y_ID: error_wr = 'h1; 76 | IRQ_RD_STATUS: error_wr = 'h1; 77 | IRQ_RD_MUX: next_irq_mux = s_irq_ni_mux_t'(csr_req_i.data_in); 78 | IRQ_RD_MASK: next_irq_mask = csr_req_i.data_in; 79 | IRQ_PULSE_ACK: reset_ack = 1; 80 | default: error_wr = 'h1; 81 | endcase 82 | /* verilator lint_on WIDTH */ 83 | end 84 | end 85 | 86 | always_comb begin : csr_decoder_r 87 | error_rd = '0; 88 | decoded_data = mux_out_ff; 89 | 90 | if (csr_req_i.valid && ~csr_req_i.rd_or_wr) begin 91 | /* verilator lint_off WIDTH */ 92 | unique case(csr_req_i.addr-(`AXI_CSR_BASE_ADDR & 16'hFFFF)) 93 | RAVENOC_VERSION: decoded_data = RavenocLabel; 94 | ROUTER_ROW_X_ID: decoded_data = ROUTER_X_ID; 95 | ROUTER_COL_Y_ID: decoded_data = ROUTER_Y_ID; 96 | IRQ_RD_STATUS: decoded_data = irqs_out_o.irq_vcs; 97 | IRQ_RD_MUX: decoded_data = irq_mux_ff; 98 | IRQ_RD_MASK: decoded_data = irq_mask_ff; 99 | BUFFER_FULL: decoded_data = full_wr_fifo_i; 100 | default: begin 101 | error_rd = 'h1; 102 | for(int i=0;i= irq_mask_ff); 141 | end 142 | end 143 | PULSE_HEAD_FLIT: begin 144 | for (int i=0;i 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 14.12.2022 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import random 10 | import cocotb 11 | import os 12 | import logging 13 | import pytest 14 | 15 | from common_noc.testbench import Tb 16 | from common_noc.constants import noc_const 17 | from cocotb_test.simulator import run 18 | from cocotb.regression import TestFactory 19 | from random import randint, randrange, getrandbits 20 | from cocotbext.axi import AxiResp,AxiBurstType 21 | import itertools 22 | 23 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 24 | noc_flavor = os.getenv("FLAVOR") 25 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 26 | 27 | # Setup testbench 28 | idle = "no_idle" if idle_inserter == None else "w_idle" 29 | backp = "no_backpressure" if backpressure_inserter == None else "w_backpressure" 30 | tb = Tb(dut, f"sim_{config_clk}_{idle}_{backp}", noc_cfg) 31 | tb.set_idle_generator(idle_inserter) 32 | tb.set_backpressure_generator(backpressure_inserter) 33 | await tb.setup_clks(config_clk) 34 | await tb.arst(config_clk) 35 | 36 | payload = bytearray("Test",'utf-8') 37 | # Valid write region 38 | # Not expecting any error 39 | result = await tb.write(sel=randrange(0,noc_cfg['max_nodes']), 40 | address=noc_cfg['vc_w_id'][randrange(0,noc_cfg['n_virt_chn'])], 41 | data=payload) 42 | assert result.resp == AxiResp.OKAY, "AXI should not raise an error on this txn" 43 | 44 | # Invalid memory address WR - out of range 45 | await tb.arst(config_clk) 46 | rand_addr = randrange(0,2**32) 47 | while rand_addr in noc_cfg['vc_w_id']: 48 | rand_addr = randrange(0,2**32) 49 | result = await tb.write(sel=randrange(0,noc_cfg['max_nodes']), 50 | address=rand_addr, 51 | data=payload) 52 | assert result.resp == AxiResp.SLVERR, "AXI bus should have raised an error when writing to an invalid region of memory" 53 | 54 | # Invalid memory address WR - writing in RD buffer 55 | await tb.arst(config_clk) 56 | result = await tb.write(sel=randrange(0,noc_cfg['max_nodes']), 57 | address=noc_cfg['vc_r_id'][randrange(0,noc_cfg['n_virt_chn'])], 58 | data=payload) 59 | assert result.resp == AxiResp.SLVERR, "AXI bus should have raised an error when writing to an invalid region of memory" 60 | 61 | # Invalid burst type 62 | await tb.arst(config_clk) 63 | result = await tb.write(sel=randrange(0,noc_cfg['max_nodes']), 64 | address=noc_cfg['vc_w_id'][randrange(0,noc_cfg['n_virt_chn'])], 65 | burst=AxiBurstType.FIXED, 66 | data=payload) 67 | assert result.resp == AxiResp.SLVERR, "AXI bus should have raised an error when writing with a not supported burst type" 68 | 69 | # Invalid memory address RD - reading in WR buffer 70 | await tb.arst(config_clk) 71 | sel_out = randrange(0,noc_cfg['max_nodes']) 72 | sel_in = sel_out 73 | while (sel_in == sel_out): 74 | sel_in = randrange(0,noc_cfg['max_nodes']) 75 | tb.dut.axi_sel_in.setimmediatevalue(sel_in) 76 | result = await tb.read(sel=sel_out, 77 | address=noc_cfg['vc_w_id'][randrange(0,noc_cfg['n_virt_chn'])], 78 | length=0x1) 79 | assert result.resp == AxiResp.SLVERR, "AXI bus should have raised an error when reading to an invalid region of memory" 80 | 81 | # Invalid memory address RD - reading out of range 82 | await tb.arst(config_clk) 83 | sel_out = randrange(0,noc_cfg['max_nodes']) 84 | tb.dut.axi_sel_in.setimmediatevalue(sel_in) 85 | rand_addr = randrange(0,2**32) 86 | while rand_addr in noc_cfg['vc_r_id']: 87 | rand_addr = randrange(0,2**32) 88 | result = await tb.read(sel=sel_out, 89 | address=rand_addr, 90 | length=0x1) 91 | assert result.resp == AxiResp.SLVERR, "AXI bus should have raised an error when reading to an invalid region of memory" 92 | 93 | # Valid read region but empty 94 | # [DEPRECATED] The following test is deprecated due to the changes of multiple bursts per pkt 95 | # await tb.arst(config_clk) 96 | # sel_out = randrange(0,noc_cfg['max_nodes']) 97 | # sel_in = sel_out 98 | # while (sel_in == sel_out): 99 | # sel_in = randrange(0,noc_cfg['max_nodes']) 100 | # tb.dut.axi_sel_in.setimmediatevalue(sel_in) 101 | # result = await tb.read(sel=sel_out, 102 | # address=noc_cfg['vc_r_id'][randrange(0,noc_cfg['n_virt_chn'])], 103 | # length=0x1) 104 | # assert result.resp == AxiResp.SLVERR, "AXI should have raise an error on this txn" 105 | 106 | def cycle_pause(): 107 | return itertools.cycle([1, 1, 1, 0]) 108 | 109 | if cocotb.SIM_NAME: 110 | factory = TestFactory(run_test) 111 | factory.add_option("config_clk", ["AXI_slwT_NoC", "NoC_slwT_AXI", "NoC_equal_AXI"]) 112 | factory.add_option("idle_inserter", [None, cycle_pause]) 113 | factory.add_option("backpressure_inserter", [None, cycle_pause]) 114 | factory.generate_tests() 115 | 116 | @pytest.mark.parametrize("flavor",noc_const.regression_setup) 117 | def test_wrong_ops(flavor): 118 | """ 119 | Checks if the AXI-S/NoC is capable of throwing an errors when illegal operations are executed 120 | 121 | Test ID: 2 122 | 123 | Description: 124 | Different AXI-S txn are request on the routers to check if wrong/illegal txn are not allowed to move forward in the NoC. It's expected 125 | the NoC/AXI slave interface to refuse the txn throwing an error on the slave interface due to not supported requests. Here's a list of 126 | all txns that are sent over this test: 127 | - Write: Invalid memory address - out of range or not mapped 128 | - Write: Writing on read buffer region 129 | - Write: Invalid burst type = FIXED 130 | - Read: Reading from write buffer region 131 | - Read: Reading from an invalid memory region - out of range or not mapped 132 | - Read: Just after the reset, reading from empty buffer 133 | 134 | We don't check if the write on full buffer will thrown an error because by uArch the NoC will generate back pressure on the master if the 135 | buffers are full and more incoming txns are being requested at the w.address channel. 136 | """ 137 | module = os.path.splitext(os.path.basename(__file__))[0] 138 | SIM_BUILD = os.path.join(noc_const.TESTS_DIR, f"../../run_dir/sim_build_{noc_const.SIMULATOR}_{module}_{flavor}") 139 | noc_const.EXTRA_ENV['SIM_BUILD'] = SIM_BUILD 140 | noc_const.EXTRA_ENV['FLAVOR'] = flavor 141 | 142 | extra_args_sim = noc_const._get_cfg_args(flavor) 143 | 144 | run( 145 | python_search=[noc_const.TESTS_DIR], 146 | includes=noc_const.INC_DIR, 147 | verilog_sources=noc_const.VERILOG_SOURCES, 148 | toplevel=noc_const.TOPLEVEL, 149 | module=module, 150 | sim_build=SIM_BUILD, 151 | extra_env=noc_const.EXTRA_ENV, 152 | extra_args=extra_args_sim 153 | ) 154 | -------------------------------------------------------------------------------- /tb/common_noc/ravenoc_pkt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : ravenoc_pkt.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 17.03.2021 7 | # Last Modified Date: 17.03.2021 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import cocotb 10 | import logging 11 | import math 12 | import random 13 | import string 14 | from common_noc.constants import noc_const 15 | from cocotb.clock import Clock 16 | from cocotb.triggers import ClockCycles, FallingEdge, RisingEdge, Timer 17 | from cocotb.log import SimColourLogFormatter, SimLog, SimTimeContextFilter 18 | from datetime import datetime 19 | from random import randint, randrange, getrandbits 20 | 21 | class RaveNoC_pkt: 22 | """ 23 | Base class for RaveNoC pkt creation 24 | 25 | Args: 26 | cfg: Configuration dictionary for the specific hardware NoC on test 27 | msg: Message in string format that'll compose the payload of the packet 28 | if the message cannot fit into a single pkt, the head flit will contain random 29 | data and the message will be send in the following flits. 30 | src_dest: Sets the source node that's sending the flit, it's needed to select 31 | the correct input mux when writing the flits. Sets also the destination node 32 | that'll receive the pkt, also used in the pkt 33 | creation to assemble the head flit 34 | virt_chn_id: Virtual channel used to send the flit over the NoC, required to 35 | define which AXI address we should use to read/write 36 | """ 37 | def __init__(self, cfg, msg=None, src_dest=(None,None), virt_chn_id=None): 38 | # Head Flit: 39 | # -> Considering coffee/vanilla flavors, the head flit can be written as: 40 | # 1) Coffee: 41 | # 65---------------63-----------------------------------------------------------0 42 | # | FLIT_TYPE (2b) | X_DEST (2b) | Y_DEST (2b) | PKT_WIDTH (8b) | MESSAGE (52b) | 43 | # +-----------------------------------------------------------------------------+ 44 | # FLIT_TYPE is prepended by the NoC 45 | # 2) Vanilla: 46 | # 33---------------31-----------------------------------------------------------0 47 | # | FLIT_TYPE (2b) | X_DEST (1b) | Y_DEST (1b) | PKT_WIDTH (8b) | MESSAGE (20b) | 48 | # +-----------------------------------------------------------------------------+ 49 | # FLIT_TYPE is prepended by the NoC 50 | # It's required to apply a mask on top of the initial flit head msg 51 | # To make things simpler, if message size is smaller or equal to the size 52 | # of min num bytes into a single flit, we concatenate in the head flit 53 | # otherwise we add some random data on head flit and send the message in 54 | # the following flits (body+tail) 55 | 56 | # Max width in bits of head flit msg 57 | self.max_hflit_w = cfg['flit_data_width']-cfg['x_w']-cfg['y_w']-cfg['sz_pkt_w'] 58 | # Max width in bytes of head flit msg 59 | self.max_bytes_hflit = math.floor(self.max_hflit_w/8) 60 | self.cfg = cfg 61 | if msg == None: 62 | msg = self._get_random_msg(self.max_bytes_hflit) 63 | if virt_chn_id == None: 64 | virt_chn_id = self._get_random_vc() 65 | if src_dest == (None,None): 66 | src_dest = self._get_random_src_dest() # it means we need to initialize it 67 | assert src_dest[0] != src_dest[1], "A RaveNoC pkt cannot have src == dest!" 68 | length_bytes = len(msg) 69 | x_src, y_src = self._get_coord(src_dest[0], cfg) 70 | x_dest, y_dest = self._get_coord(src_dest[1], cfg) 71 | self.src = (src_dest[0],x_src,y_src) 72 | self.dest = (src_dest[1],x_dest,y_dest) 73 | self.axi_address_w = cfg['vc_w_id'][virt_chn_id] 74 | self.axi_address_r = cfg['vc_r_id'][virt_chn_id] 75 | num_bytes_per_flit = int(cfg['flit_data_width']/8) 76 | self.num_bytes_per_beat = num_bytes_per_flit 77 | if length_bytes <= self.max_bytes_hflit: 78 | self.msg = bytearray(msg,'utf-8') 79 | self.length = num_bytes_per_flit 80 | # This value can vary from 1 (single head flit) up to MAX, where MAX=255 81 | # actually MAX will be 256 because 255 data + 1 head flit but if we overflow 82 | # we mess with the y dest of the pkt 83 | self.length_beats = 0 84 | msg_hflit = 0 85 | msg_hflit = int.from_bytes(self.msg,byteorder="big") 86 | self.hflit = msg_hflit 87 | self.hflit = self.hflit | (self.length_beats << (self.max_hflit_w)) 88 | self.hflit = self.hflit | (y_dest << (cfg['sz_pkt_w']+self.max_hflit_w)) 89 | self.hflit = self.hflit | (x_dest << (cfg['y_w']+cfg['sz_pkt_w']+self.max_hflit_w)) 90 | self.msg = bytearray(self.hflit.to_bytes(num_bytes_per_flit,"little")) 91 | else: 92 | # Length is always in bytes 93 | self.length = (1+math.ceil(length_bytes/num_bytes_per_flit))*num_bytes_per_flit 94 | self.length_beats = 0xFF & (int(self.length/self.num_bytes_per_beat)-1) 95 | msg_hflit = randrange(0, (self.max_bytes_hflit*(256))-1) 96 | self.hflit = msg_hflit 97 | self.hflit = self.hflit | (self.length_beats << (self.max_hflit_w)) 98 | self.hflit = self.hflit | (y_dest << (cfg['sz_pkt_w']+self.max_hflit_w)) 99 | self.hflit = self.hflit | (x_dest << (cfg['y_w']+cfg['sz_pkt_w']+self.max_hflit_w)) 100 | # We need to pad with zero chars to match bus data width once each beat of burst is full width 101 | if length_bytes%num_bytes_per_flit != 0: 102 | while len(msg)%num_bytes_per_flit != 0: 103 | msg += '\0' 104 | msg = bytearray(msg,'utf-8') 105 | self.msg = bytearray(self.hflit.to_bytes(num_bytes_per_flit,"little")) + msg 106 | self.virt_chn_id = virt_chn_id 107 | 108 | """ 109 | Method to convert from single flat address in the NoC to row/col (x/y) coord. 110 | 111 | Args: 112 | node: Absolute address between 0 to max number of nodes in the NoC 113 | noc_cfg: Hardware of the NoC in test used to compute the x,y parameters 114 | Returns: 115 | noc_enc[node]: Return a list with the coord. row/col (x/y) inside the NoC 116 | """ 117 | def _get_coord(self, node, noc_cfg): 118 | noc_enc, row, col = [], 0, 0 119 | for i in range(noc_cfg['max_nodes']): 120 | noc_enc.append([row,col]) 121 | if col == (noc_cfg['noc_cfg_sz_cols']-1): 122 | row, col = row+1, 0 123 | else: 124 | col += 1 125 | return noc_enc[node] 126 | 127 | """ 128 | Helper method to get random src/dest for the the pkt 129 | """ 130 | def _get_random_src_dest(self): 131 | rnd_src = randrange(0, self.cfg['max_nodes']) 132 | rnd_dest = randrange(0, self.cfg['max_nodes']) 133 | while rnd_dest == rnd_src: 134 | rnd_dest = randrange(0, self.cfg['max_nodes']) 135 | return (rnd_src,rnd_dest) 136 | 137 | """ 138 | Helper method to get random virtual channel for the the pkt 139 | """ 140 | def _get_random_vc(self): 141 | vc_id = randrange(0, len(self.cfg['vc_w_id'])) 142 | return vc_id 143 | 144 | """ 145 | Helper method to get random message with number of bytes equal to length 146 | """ 147 | def _get_random_msg(self, length=1): 148 | # choose from all lowercase letter 149 | letters = string.ascii_lowercase 150 | result_str = ''.join(random.choice(letters) for i in range(length)) 151 | return result_str 152 | -------------------------------------------------------------------------------- /tb/test_noc_csr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : test_noc_csr.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 25.06.2023 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import random 10 | import cocotb 11 | import os 12 | import logging 13 | import pytest 14 | 15 | from common_noc.testbench import Tb 16 | from common_noc.constants import noc_const 17 | from common_noc.ravenoc_pkt import RaveNoC_pkt 18 | from cocotb_test.simulator import run 19 | from cocotb.regression import TestFactory 20 | from random import randrange 21 | from cocotb.result import TestFailure 22 | from cocotbext.axi import AxiResp,AxiBurstType 23 | import itertools 24 | 25 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 26 | noc_flavor = os.getenv("FLAVOR") 27 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 28 | 29 | # Setup testbench 30 | idle = "no_idle" if idle_inserter == None else "w_idle" 31 | backp = "no_backpressure" if backpressure_inserter == None else "w_backpressure" 32 | tb = Tb(dut, f"sim_{config_clk}_{idle}_{backp}", noc_cfg) 33 | tb.set_idle_generator(idle_inserter) 34 | tb.set_backpressure_generator(backpressure_inserter) 35 | await tb.setup_clks(config_clk) 36 | await tb.arst(config_clk) 37 | csr = noc_const.NOC_CSRs 38 | 39 | # RaveNoC Version 40 | router = randrange(0,noc_cfg['max_nodes']) 41 | resp = await tb.read(sel=router, address=csr['RAVENOC_VERSION'], length=4, size=0x2) 42 | assert resp.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 43 | version = resp.data.decode()[::-1] # get object data, convert from bytearray to string with decode method and invert it 44 | assert version == noc_const.NOC_VERSION, "NoC version not matching with expected!" 45 | tb.log.info("NoC Version = %s",version) 46 | 47 | # Router X,Y coordinates check 48 | router = randrange(0,noc_cfg['max_nodes']) 49 | ref_pkt = RaveNoC_pkt(cfg=noc_cfg, src_dest=(router,0 if router !=0 else 1)) # pkt not used, only to compare in the assertion 50 | resp_row = await tb.read(sel=router, address=csr['ROUTER_ROW_X_ID'], length=4, size=0x2) 51 | resp_col = await tb.read(sel=router, address=csr['ROUTER_COL_Y_ID'], length=4, size=0x2) 52 | assert resp_row.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 53 | assert resp_col.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 54 | resp_row = int.from_bytes(resp_row.data, byteorder='little', signed=False) 55 | resp_col = int.from_bytes(resp_col.data, byteorder='little', signed=False) 56 | assert resp_row == ref_pkt.src[1], "NoC CSR - Coordinate ROW not matching" 57 | assert resp_col == ref_pkt.src[2], "NoC CSR - Coordinate COL not matching" 58 | 59 | # IRQ registers 60 | router = randrange(0,noc_cfg['max_nodes']) 61 | resp = await tb.read(sel=router, address=csr['IRQ_RD_STATUS'], length=4, size=0x2) 62 | assert resp.resp == AxiResp.OKAY, "AXI bus should have raised an error here!" 63 | irq = resp.data.decode()[::-1] 64 | 65 | router = randrange(0,noc_cfg['max_nodes']) 66 | rand_data = bytearray(tb._get_random_string(length=4),'utf-8') 67 | req = await tb.write(sel=router, address=csr['IRQ_RD_MUX'], data=rand_data, size=0x2) 68 | assert req.resp == AxiResp.OKAY, "AXI bus should have not raised an error here!" 69 | resp = await tb.read(sel=router, address=csr['IRQ_RD_MUX'], length=4, size=0x2) 70 | assert resp.resp == AxiResp.OKAY, "AXI bus should have raised an error here!" 71 | # We need to do the & 7 masking because this CSR has only 3-bits 72 | data_in = int.from_bytes(rand_data, byteorder='little', signed=False) & 7 73 | data_out = int.from_bytes(resp.data, byteorder='little', signed=False) 74 | assert data_in == data_out, "NoC CSR, mismatch on IRQ_RD_MUX - Write/Read back" 75 | 76 | router = randrange(0,noc_cfg['max_nodes']) 77 | rand_data = bytearray(tb._get_random_string(length=4),'utf-8') 78 | req = await tb.write(sel=router, address=csr['IRQ_RD_MASK'], data=rand_data, size=0x2) 79 | assert req.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 80 | resp = await tb.read(sel=router, address=csr['IRQ_RD_MASK'], length=4, size=0x2) 81 | assert resp.resp == AxiResp.OKAY, "AXI bus should have raised an error here!" 82 | data_in = int.from_bytes(rand_data, byteorder='little', signed=False) 83 | data_out = int.from_bytes(resp.data, byteorder='little', signed=False) 84 | assert data_in == data_out, "NoC CSR, mismatch on IRQ_RD_MASK - Write/Read back" 85 | 86 | # Illegal operations 87 | not_writable = [csr['RAVENOC_VERSION'], 88 | csr['ROUTER_ROW_X_ID'], 89 | csr['ROUTER_COL_Y_ID'], 90 | csr['IRQ_RD_STATUS'], 91 | csr['RD_SIZE_VC_START'], 92 | csr['WR_BUFFER_FULL']] 93 | not_writable.extend([(csr['RD_SIZE_VC_START']+4*x) for x in range(noc_cfg['n_virt_chn'])]) 94 | router = randrange(0,noc_cfg['max_nodes']) 95 | rand_data = bytearray(tb._get_random_string(length=4),'utf-8') 96 | for not_wr in not_writable: 97 | req = await tb.write(sel=router, address=not_wr, data=rand_data, size=0x2) 98 | assert req.resp == AxiResp.SLVERR, "AXI bus should have raised an error here! ILLEGAL WR CSR:"+hex(not_wr) 99 | 100 | router = randrange(0,noc_cfg['max_nodes']) 101 | 102 | if noc_cfg['flit_data_width'] == 64: 103 | for i in csr: 104 | req = await tb.read(sel=router, address=csr[i], size=0x3) 105 | assert req.resp == AxiResp.SLVERR, "AXI bus should have raised an error here!" 106 | req = await tb.write(sel=router, address=csr[i], data=rand_data, size=0x3) 107 | assert req.resp == AxiResp.SLVERR, "AXI bus should have raised an error here!" 108 | 109 | # Testing RD_SIZE_VC[0,1,2...]_PKT 110 | for vc in range(noc_cfg['n_virt_chn']): 111 | await tb.arst(config_clk) 112 | msg_size = randrange(5,noc_cfg['max_sz_pkt']) 113 | msg = tb._get_random_string(length=msg_size) 114 | pkt = RaveNoC_pkt(cfg=noc_cfg, msg=msg, virt_chn_id=vc) 115 | write = cocotb.fork(tb.write_pkt(pkt, timeout=noc_const.TIMEOUT_AXI_EXT)) 116 | await tb.wait_irq() 117 | resp_csr = await tb.read(sel=pkt.dest[0], address=(csr['RD_SIZE_VC_START']+4*vc), length=4, size=0x2) 118 | resp_pkt_size = int.from_bytes(resp_csr.data, byteorder='little', signed=False) 119 | assert resp_pkt_size == pkt.length_beats, "Mistmatch on CSR pkt size vs pkt sent!" 120 | resp = await tb.read_pkt(pkt, timeout=noc_const.TIMEOUT_AXI_EXT) 121 | tb.check_pkt(resp.data,pkt.msg) 122 | 123 | def cycle_pause(): 124 | return itertools.cycle([1, 1, 1, 0]) 125 | 126 | if cocotb.SIM_NAME: 127 | factory = TestFactory(run_test) 128 | factory.add_option("config_clk", ["AXI_slwT_NoC", "NoC_slwT_AXI", "NoC_equal_AXI"]) 129 | factory.add_option("idle_inserter", [None, cycle_pause]) 130 | factory.add_option("backpressure_inserter", [None, cycle_pause]) 131 | factory.generate_tests() 132 | 133 | @pytest.mark.parametrize("flavor",noc_const.regression_setup) 134 | def test_noc_csr(flavor): 135 | """ 136 | Check all WR/RD CSRs inside the NoC 137 | 138 | Test ID: 7 139 | 140 | Description: 141 | Write/Read to all CSRs of the NoC. It's also write in READ only registers and 142 | check if DWORD operations are answered with errors too. 143 | """ 144 | module = os.path.splitext(os.path.basename(__file__))[0] 145 | SIM_BUILD = os.path.join(noc_const.TESTS_DIR, 146 | f"../../run_dir/sim_build_{noc_const.SIMULATOR}_{module}_{flavor}") 147 | noc_const.EXTRA_ENV['SIM_BUILD'] = SIM_BUILD 148 | noc_const.EXTRA_ENV['FLAVOR'] = flavor 149 | 150 | extra_args_sim = noc_const._get_cfg_args(flavor) 151 | 152 | run( 153 | python_search=[noc_const.TESTS_DIR], 154 | includes=noc_const.INC_DIR, 155 | verilog_sources=noc_const.VERILOG_SOURCES, 156 | toplevel=noc_const.TOPLEVEL, 157 | module=module, 158 | sim_build=SIM_BUILD, 159 | extra_env=noc_const.EXTRA_ENV, 160 | extra_args=extra_args_sim 161 | ) 162 | -------------------------------------------------------------------------------- /tb/test_irqs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : test_irqs.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 16.11.2024 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import random 10 | import cocotb 11 | import os 12 | import logging 13 | import pytest 14 | 15 | from common_noc.testbench import Tb 16 | from common_noc.constants import noc_const 17 | from common_noc.ravenoc_pkt import RaveNoC_pkt 18 | from cocotb_test.simulator import run 19 | from cocotb.regression import TestFactory 20 | from random import randrange 21 | from cocotb.result import TestFailure 22 | from cocotb.triggers import ClockCycles 23 | from cocotbext.axi import AxiResp,AxiBurstType 24 | import itertools 25 | 26 | async def run_test(dut, config_clk="NoC_slwT_AXI", idle_inserter=None, backpressure_inserter=None): 27 | noc_flavor = os.getenv("FLAVOR") 28 | noc_cfg = noc_const.NOC_CFG[noc_flavor] 29 | 30 | # Setup testbench 31 | idle = "no_idle" if idle_inserter == None else "w_idle" 32 | backp = "no_backpressure" if backpressure_inserter == None else "w_backpressure" 33 | tb = Tb(dut, f"sim_{config_clk}_{idle}_{backp}", noc_cfg) 34 | tb.set_idle_generator(idle_inserter) 35 | tb.set_backpressure_generator(backpressure_inserter) 36 | await tb.setup_clks(config_clk) 37 | await tb.arst(config_clk) 38 | csr = noc_const.NOC_CSRs 39 | 40 | 41 | encode_mux = {} 42 | encode_mux['DEFAULT'] = bytearray(0) 43 | encode_mux['MUX_EMPTY_FLAGS'] = bytearray([1,0,0,0]) 44 | encode_mux['MUX_FULL_FLAGS'] = bytearray([2,0,0,0]) 45 | encode_mux['MUX_COMP_FLAGS'] = bytearray([3,0,0,0]) 46 | encode_mux['PULSE_HEAD_FLIT'] = bytearray([4,0,0,0]) 47 | 48 | # Check MUX_EMPTY_FLAGS 49 | pkt = RaveNoC_pkt(cfg=noc_cfg, virt_chn_id=0) 50 | # First we setup the dest router with the correct switch 51 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MUX'], data=encode_mux['MUX_EMPTY_FLAGS'], size=0x2) 52 | assert req.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 53 | await tb.write_pkt(pkt) 54 | irq_wait = (2**(pkt.virt_chn_id)) << (pkt.dest[0]*noc_cfg['n_virt_chn']) 55 | tb.log.info("IRQ val to wait: %d",irq_wait) 56 | await tb.wait_irq_x(irq_wait) 57 | # Now we use the mask to disable all the IRQs 58 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([0,0,0,0]), size=0x2) 59 | assert tb.dut.irqs_out.value == 0, "No IRQs should be triggered on this scenario" 60 | # ... and we re-enable all the IRQs 61 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([0xFF,0xFF,0xFF,0xFF]), size=0x2) 62 | assert tb.dut.irqs_out.value != 0, "IRQs should be triggered on this scenario" 63 | 64 | # Check MUX_FULL_FLAGS 65 | await tb.arst(config_clk) 66 | pkt = RaveNoC_pkt(cfg=noc_cfg) 67 | pkt_copy = [pkt for i in range(0,buff_rd_vc(pkt.virt_chn_id))] 68 | # First we setup the dest router with the correct switch 69 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MUX'], data=encode_mux['MUX_FULL_FLAGS'], size=0x2) 70 | assert req.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 71 | await tb.write_pkt(pkt) 72 | # Wait some time to ensure the pkt has arrived in the dest 73 | await ClockCycles(tb.dut.clk_noc, 15) 74 | if (buff_rd_vc(pkt.virt_chn_id) != 1): 75 | assert tb.dut.irqs_out.value == 0, "No IRQs should be triggered on this scenario" 76 | await tb.read_pkt(pkt) 77 | # Now we don't have more pkts 78 | for pkt in pkt_copy: 79 | await tb.write_pkt(pkt) 80 | assert req.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 81 | irq_wait = (2**(pkt.virt_chn_id)) << (pkt.dest[0]*noc_cfg['n_virt_chn']) 82 | tb.log.info("IRQ val to wait: %d",irq_wait) 83 | await tb.wait_irq_x(irq_wait) 84 | # Now we use the mask to disable all the IRQs 85 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([0,0,0,0]), size=0x2) 86 | assert tb.dut.irqs_out.value == 0, "No IRQs should be triggered on this scenario" 87 | # ... and we re-enable all the IRQs 88 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([0xFF,0xFF,0xFF,0xFF]), size=0x2) 89 | assert tb.dut.irqs_out.value != 0, "IRQs should be triggered on this scenario" 90 | 91 | # Check MUX_COMP_FLAGS 92 | await tb.arst(config_clk) 93 | pkt = RaveNoC_pkt(cfg=noc_cfg) 94 | pkt_copy = [pkt for i in range(0,buff_rd_vc(pkt.virt_chn_id))] 95 | # First we setup the dest router with the correct switch 96 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MUX'], data=encode_mux['MUX_COMP_FLAGS'], size=0x2) 97 | assert req.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 98 | # In COMP mode, MASK will work as ref. for comparison, thus we set it to ZERO, so every buff will trigger it 99 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([0,0,0,0]), size=0x2) 100 | await tb.write_pkt(pkt) 101 | await tb.wait_irq() 102 | assert tb.dut.irqs_out.value != 0, "IRQs should be triggered on this scenario" 103 | if (buff_rd_vc(pkt.virt_chn_id) != 1): 104 | # Now we change the comp. to >= 2 105 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([2,0,0,0]), size=0x2) 106 | assert tb.dut.irqs_out.value == 0, "No IRQs should be triggered on this scenario" #...bc we only sent one pkt 107 | # and we send another pkt 108 | await tb.write_pkt(pkt) 109 | # On this case IRQ should be triggered once we have >= 2 pkt sent 110 | await tb.wait_irq() 111 | 112 | # Check PULSE_HEAD_FLIT 113 | pkt = RaveNoC_pkt(cfg=noc_cfg, virt_chn_id=0) 114 | # First we setup the dest router with the correct switch 115 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MUX'], data=encode_mux['PULSE_HEAD_FLIT'], size=0x2) 116 | assert req.resp == AxiResp.OKAY, "AXI bus should not have raised an error here!" 117 | await tb.write_pkt(pkt) 118 | irq_wait = (2**(pkt.virt_chn_id)) << (pkt.dest[0]*noc_cfg['n_virt_chn']) 119 | tb.log.info("IRQ val to wait: %d",irq_wait) 120 | # await tb.wait_irq_x(irq_wait) 121 | # Now we use the ACK to disable all the IRQs 122 | req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_PULSE_ACK'], data=bytearray([0,0,0,0]), size=0x2) 123 | # assert tb.dut.irqs_out.value == 0, "No IRQs should be triggered on this scenario" 124 | # ... and we re-enable all the IRQs 125 | # req = await tb.write(sel=pkt.dest[0], address=csr['IRQ_RD_MASK'], data=bytearray([0xFF,0xFF,0xFF,0xFF]), size=0x2) 126 | # assert tb.dut.irqs_out.value != 0, "IRQs should be triggered on this scenario" 127 | 128 | def buff_rd_vc(x): 129 | return (1< 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 09.03.2021 7 | # Last Modified Date: 25.06.2023 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import os 10 | import glob 11 | import copy 12 | import math 13 | 14 | class noc_const: 15 | regression_setup = ['vanilla', 'coffee'] 16 | if os.getenv("FULL_REGRESSION"): 17 | regression_setup.append('liquorice') 18 | 19 | # NoC CSRs addresses 20 | NOC_CSRs = {} 21 | NOC_CSRs['RAVENOC_VERSION'] = 0x3000 22 | NOC_CSRs['ROUTER_ROW_X_ID'] = 0x3004 23 | NOC_CSRs['ROUTER_COL_Y_ID'] = 0x3008 24 | NOC_CSRs['IRQ_RD_STATUS'] = 0x300c 25 | NOC_CSRs['IRQ_RD_MUX'] = 0x3010 26 | NOC_CSRs['IRQ_RD_MASK'] = 0x3014 27 | NOC_CSRs['WR_BUFFER_FULL'] = 0x3018 28 | NOC_CSRs['IRQ_PULSE_ACK'] = 0x301c 29 | NOC_CSRs['RD_SIZE_VC_START'] = 0x3020 30 | 31 | NOC_VERSION = "v1.0" 32 | CLK_100MHz = (10, "ns") 33 | CLK_200MHz = (5, "ns") 34 | RST_CYCLES = 3 35 | TIMEOUT_AXI = (CLK_100MHz[0]*200, "ns") 36 | TIMEOUT_AXI_EXT = (CLK_200MHz[0]*5000, "ns") 37 | TIMEOUT_IRQ_V = 100 38 | TIMEOUT_IRQ = (CLK_100MHz[0]*100, "ns") 39 | 40 | TESTS_DIR = os.path.dirname(os.path.abspath(__file__)) 41 | RTL_DIR = os.path.join(TESTS_DIR,"../../src/") 42 | INC_DIR = [f'{RTL_DIR}include'] 43 | TOPLEVEL = str(os.getenv("DUT")) 44 | SIMULATOR = str(os.getenv("SIM")) 45 | VERILOG_SOURCES = [] # The sequence below is important... 46 | VERILOG_SOURCES = VERILOG_SOURCES + glob.glob(f'{RTL_DIR}include/ravenoc_defines.svh',recursive=True) 47 | VERILOG_SOURCES = VERILOG_SOURCES + glob.glob(f'bus_arch_sv_pkg/amba_axi_pkg.sv',recursive=True) 48 | VERILOG_SOURCES = VERILOG_SOURCES + glob.glob(f'{RTL_DIR}include/ravenoc_structs.svh',recursive=True) 49 | VERILOG_SOURCES = VERILOG_SOURCES + glob.glob(f'{RTL_DIR}include/ravenoc_axi_fnc.svh',recursive=True) 50 | VERILOG_SOURCES = VERILOG_SOURCES + glob.glob(f'{RTL_DIR}include/*.sv',recursive=True) 51 | VERILOG_SOURCES = VERILOG_SOURCES + glob.glob(f'{RTL_DIR}**/*.sv',recursive=True) 52 | EXTRA_ENV = {} 53 | EXTRA_ENV['COCOTB_HDL_TIMEUNIT'] = os.getenv("TIMEUNIT") 54 | EXTRA_ENV['COCOTB_HDL_TIMEPRECISION'] = os.getenv("TIMEPREC") 55 | if SIMULATOR == "verilator": 56 | #EXTRA_ARGS = ["--trace-fst","--trace-structs","--Wno-UNOPTFLAT","--Wno-REDEFMACRO"] 57 | #EXTRA_ARGS = ["--threads 4","--trace-fst","--trace-structs","--Wno-UNOPTFLAT","--Wno-REDEFMACRO"] 58 | EXTRA_ARGS = ["--trace-fst","--coverage","--coverage-line","--coverage-toggle","--trace-structs","--Wno-UNOPTFLAT","--Wno-REDEFMACRO"] 59 | elif SIMULATOR == "icarus": 60 | EXTRA_ARGS = ["-g2012"] 61 | elif SIMULATOR == "xcelium" or SIMULATOR == "ius": 62 | EXTRA_ARGS = [" -64bit \ 63 | -smartlib \ 64 | -smartorder \ 65 | -gui \ 66 | -clean \ 67 | -sv" ] 68 | else: 69 | EXTRA_ARGS = [] 70 | 71 | NOC_CFG = {} 72 | 73 | # Vanilla / Coffee HW mux 74 | # We need to use deepcopy bc of weak shallow copy of reference from python 75 | EXTRA_ARGS_VANILLA = copy.deepcopy(EXTRA_ARGS) 76 | EXTRA_ARGS_COFFEE = copy.deepcopy(EXTRA_ARGS) 77 | EXTRA_ARGS_LIQ = copy.deepcopy(EXTRA_ARGS) 78 | NOC_CFG_COFFEE = {} 79 | NOC_CFG_VANILLA = {} 80 | NOC_CFG_LIQ = {} 81 | 82 | #-------------------------- 83 | # 84 | # Parameters that'll change the HW 85 | # 86 | #------------------------- 87 | 88 | #-------------------------- 89 | # Vanilla 90 | #------------------------- 91 | #NoC width of AXI+NoC_DATA 92 | NOC_CFG_VANILLA['flit_data_width'] = 32 93 | #NoC routing algorithm 94 | NOC_CFG_VANILLA['routing_alg'] = "XYAlg" 95 | #NoC X and Y dimensions 96 | NOC_CFG_VANILLA['noc_cfg_sz_rows'] = 2 # Number of row/lines 97 | NOC_CFG_VANILLA['noc_cfg_sz_cols'] = 2 # Number of cols 98 | #NoC per InputBuffer buffering 99 | NOC_CFG_VANILLA['flit_buff'] = 2 100 | # Max number of flits per packet 101 | NOC_CFG_VANILLA['max_sz_pkt'] = 256 102 | # Number of virtual channels 103 | NOC_CFG_VANILLA['n_virt_chn'] = 2 104 | # Priority level of VCs - 0=0 has high prior / 1=0 has lower prior 105 | NOC_CFG_VANILLA['h_priority'] = "ZeroHighPrior" 106 | 107 | #-------------------------- 108 | # Coffee 109 | #------------------------- 110 | #NoC width of AXI+NoC_DATA 111 | NOC_CFG_COFFEE['flit_data_width'] = 64 112 | #NoC routing algorithm 113 | NOC_CFG_COFFEE['routing_alg'] = "YXAlg" 114 | #NoC X and Y dimensions 115 | NOC_CFG_COFFEE['noc_cfg_sz_rows'] = 4 # Number of row/lines 116 | NOC_CFG_COFFEE['noc_cfg_sz_cols'] = 4 # Number of cols 117 | #NoC per InputBuffer buffering 118 | NOC_CFG_COFFEE['flit_buff'] = 2 119 | # Max number of flits per packet 120 | NOC_CFG_COFFEE['max_sz_pkt'] = 256 121 | # Number of virtual channels 122 | NOC_CFG_COFFEE['n_virt_chn'] = 3 123 | # Priority level of VCs - 0=0 has high prior / 1=0 has lower prior 124 | NOC_CFG_COFFEE['h_priority'] = "ZeroLowPrior" 125 | 126 | #-------------------------- 127 | # Liquorice 128 | #------------------------- 129 | #NoC width of AXI+NoC_DATA 130 | NOC_CFG_LIQ['flit_data_width'] = 64 131 | #NoC routing algorithm 132 | NOC_CFG_LIQ['routing_alg'] = "XYAlg" 133 | #NoC X and Y dimensions 134 | NOC_CFG_LIQ['noc_cfg_sz_rows'] = 8 # Number of row/lines 135 | NOC_CFG_LIQ['noc_cfg_sz_cols'] = 8 # Number of cols 136 | #NoC per InputBuffer buffering 137 | NOC_CFG_LIQ['flit_buff'] = 4 138 | # Max number of flits per packet 139 | NOC_CFG_LIQ['max_sz_pkt'] = 256 140 | # Number of virtual channels 141 | NOC_CFG_LIQ['n_virt_chn'] = 4 142 | # Priority level of VCs - 0=0 has high prior / 1=0 has lower prior 143 | NOC_CFG_LIQ['h_priority'] = "ZeroHighPrior" 144 | 145 | for param in NOC_CFG_VANILLA.items(): 146 | EXTRA_ARGS_VANILLA.append("-D"+param[0].upper()+"="+str(param[1])) 147 | 148 | for param in NOC_CFG_COFFEE.items(): 149 | EXTRA_ARGS_COFFEE.append("-D"+param[0].upper()+"="+str(param[1])) 150 | 151 | for param in NOC_CFG_LIQ.items(): 152 | EXTRA_ARGS_LIQ.append("-D"+param[0].upper()+"="+str(param[1])) 153 | #-------------------------- 154 | # 155 | # Parameters that'll be used in tb 156 | # 157 | #------------------------- 158 | NOC_CFG_VANILLA['max_nodes'] = NOC_CFG_VANILLA['noc_cfg_sz_rows']*NOC_CFG_VANILLA['noc_cfg_sz_cols'] 159 | NOC_CFG_VANILLA['x_w'] = len(bin(NOC_CFG_VANILLA['noc_cfg_sz_rows']-1))-2 160 | NOC_CFG_VANILLA['y_w'] = len(bin(NOC_CFG_VANILLA['noc_cfg_sz_cols']-1))-2 161 | NOC_CFG_VANILLA['sz_pkt_w'] = len(bin(NOC_CFG_VANILLA['max_sz_pkt']-1))-2 162 | NOC_CFG_VANILLA['vc_w_id'] = [(x*8+(0x1000)) for x in range(NOC_CFG_VANILLA['n_virt_chn'])] 163 | NOC_CFG_VANILLA['vc_r_id'] = [(x*8+(0x2000)) for x in range(NOC_CFG_VANILLA['n_virt_chn'])] 164 | NOC_CFG['vanilla'] = NOC_CFG_VANILLA 165 | 166 | NOC_CFG_COFFEE['max_nodes'] = NOC_CFG_COFFEE['noc_cfg_sz_rows']*NOC_CFG_COFFEE['noc_cfg_sz_cols'] 167 | NOC_CFG_COFFEE['x_w'] = len(bin(NOC_CFG_COFFEE['noc_cfg_sz_rows']-1))-2 168 | NOC_CFG_COFFEE['y_w'] = len(bin(NOC_CFG_COFFEE['noc_cfg_sz_cols']-1))-2 169 | NOC_CFG_COFFEE['sz_pkt_w'] =len(bin(NOC_CFG_COFFEE['max_sz_pkt']-1))-2 170 | NOC_CFG_COFFEE['vc_w_id'] = [(x*8+(0x1000)) for x in range(NOC_CFG_COFFEE['n_virt_chn'])] 171 | NOC_CFG_COFFEE['vc_r_id'] = [(x*8+(0x2000)) for x in range(NOC_CFG_COFFEE['n_virt_chn'])] 172 | NOC_CFG['coffee'] = NOC_CFG_COFFEE 173 | 174 | NOC_CFG_LIQ['max_nodes'] = NOC_CFG_LIQ['noc_cfg_sz_rows']*NOC_CFG_LIQ['noc_cfg_sz_cols'] 175 | NOC_CFG_LIQ['x_w'] = len(bin(NOC_CFG_LIQ['noc_cfg_sz_rows']-1))-2 176 | NOC_CFG_LIQ['y_w'] = len(bin(NOC_CFG_LIQ['noc_cfg_sz_cols']-1))-2 177 | NOC_CFG_LIQ['sz_pkt_w'] =len(bin(NOC_CFG_LIQ['max_sz_pkt']-1))-2 178 | NOC_CFG_LIQ['vc_w_id'] = [(x*8+(0x1000)) for x in range(NOC_CFG_LIQ['n_virt_chn'])] 179 | NOC_CFG_LIQ['vc_r_id'] = [(x*8+(0x2000)) for x in range(NOC_CFG_LIQ['n_virt_chn'])] 180 | NOC_CFG['liquorice'] = NOC_CFG_LIQ 181 | 182 | def _get_cfg_args(flavor): 183 | if flavor == "vanilla": 184 | return noc_const.EXTRA_ARGS_VANILLA 185 | elif flavor == "coffee": 186 | return noc_const.EXTRA_ARGS_COFFEE 187 | else: 188 | return noc_const.EXTRA_ARGS_LIQ 189 | 190 | -------------------------------------------------------------------------------- /src/router/router_ravenoc.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: router_ravenoc.sv 3 | * Description: RaveNoC router datapath 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module router_ravenoc 26 | import amba_axi_pkg::*; 27 | import ravenoc_pkg::*; 28 | #( 29 | parameter logic [XWidth-1:0] ROUTER_X_ID = 0, 30 | parameter logic [YWidth-1:0] ROUTER_Y_ID = 0 31 | ) ( 32 | input clk, 33 | input arst, 34 | router_if.send_flit north_send, 35 | router_if.recv_flit north_recv, 36 | router_if.send_flit south_send, 37 | router_if.recv_flit south_recv, 38 | router_if.send_flit west_send, 39 | router_if.recv_flit west_recv, 40 | router_if.send_flit east_send, 41 | router_if.recv_flit east_recv, 42 | router_if.send_flit local_send, 43 | router_if.recv_flit local_recv, 44 | output full_wr_fifo_o 45 | ); 46 | // Mapping input modules 47 | s_flit_req_t [4:0] int_req_v; 48 | s_flit_resp_t [4:0] int_resp_v; 49 | s_router_ports_t [4:0] int_route_v; 50 | 51 | // Mapping output modules 52 | s_flit_req_t [4:0] [3:0] int_map_req_v; 53 | s_flit_resp_t [4:0] [3:0] int_map_resp_v; 54 | 55 | // External connections 56 | s_flit_req_t [4:0] ext_req_v_i; 57 | s_flit_resp_t [4:0] ext_resp_v_o; 58 | 59 | s_flit_req_t [4:0] ext_req_v_o; 60 | s_flit_resp_t [4:0] ext_resp_v_i; 61 | 62 | input_module # ( 63 | .ROUTER_X_ID (ROUTER_X_ID), 64 | .ROUTER_Y_ID (ROUTER_Y_ID) 65 | ) u_input_north ( 66 | .clk (clk), 67 | .arst (arst), 68 | .fin_req_i (ext_req_v_i[NORTH_PORT]), 69 | .fin_resp_o (ext_resp_v_o[NORTH_PORT]), 70 | .fout_req_o (int_req_v[NORTH_PORT]), 71 | .fout_resp_i (int_resp_v[NORTH_PORT]), 72 | .router_port_o(int_route_v[NORTH_PORT]), 73 | .full_o (), 74 | .empty_o () 75 | ); 76 | 77 | input_module # ( 78 | .ROUTER_X_ID (ROUTER_X_ID), 79 | .ROUTER_Y_ID (ROUTER_Y_ID) 80 | ) u_input_south ( 81 | .clk (clk), 82 | .arst (arst), 83 | .fin_req_i (ext_req_v_i[SOUTH_PORT]), 84 | .fin_resp_o (ext_resp_v_o[SOUTH_PORT]), 85 | .fout_req_o (int_req_v[SOUTH_PORT]), 86 | .fout_resp_i (int_resp_v[SOUTH_PORT]), 87 | .router_port_o(int_route_v[SOUTH_PORT]), 88 | .full_o (), 89 | .empty_o () 90 | ); 91 | 92 | input_module # ( 93 | .ROUTER_X_ID (ROUTER_X_ID), 94 | .ROUTER_Y_ID (ROUTER_Y_ID) 95 | ) u_input_west ( 96 | .clk (clk), 97 | .arst (arst), 98 | .fin_req_i (ext_req_v_i[WEST_PORT]), 99 | .fin_resp_o (ext_resp_v_o[WEST_PORT]), 100 | .fout_req_o (int_req_v[WEST_PORT]), 101 | .fout_resp_i (int_resp_v[WEST_PORT]), 102 | .router_port_o(int_route_v[WEST_PORT]), 103 | .full_o (), 104 | .empty_o () 105 | ); 106 | 107 | input_module # ( 108 | .ROUTER_X_ID (ROUTER_X_ID), 109 | .ROUTER_Y_ID (ROUTER_Y_ID) 110 | ) u_input_east ( 111 | .clk (clk), 112 | .arst (arst), 113 | .fin_req_i (ext_req_v_i[EAST_PORT]), 114 | .fin_resp_o (ext_resp_v_o[EAST_PORT]), 115 | .fout_req_o (int_req_v[EAST_PORT]), 116 | .fout_resp_i (int_resp_v[EAST_PORT]), 117 | .router_port_o(int_route_v[EAST_PORT]), 118 | .full_o (), 119 | .empty_o () 120 | ); 121 | 122 | input_module # ( 123 | .ROUTER_X_ID (ROUTER_X_ID), 124 | .ROUTER_Y_ID (ROUTER_Y_ID) 125 | ) u_input_local ( 126 | .clk (clk), 127 | .arst (arst), 128 | .fin_req_i (ext_req_v_i[LOCAL_PORT]), 129 | .fin_resp_o (ext_resp_v_o[LOCAL_PORT]), 130 | .fout_req_o (int_req_v[LOCAL_PORT]), 131 | .fout_resp_i (int_resp_v[LOCAL_PORT]), 132 | .router_port_o(int_route_v[LOCAL_PORT]), 133 | .full_o (full_wr_fifo_o), 134 | .empty_o () 135 | ); 136 | 137 | output_module u_output_north ( 138 | .clk (clk), 139 | .arst (arst), 140 | .fin_req_i (int_map_req_v[NORTH_PORT]), 141 | .fin_resp_o (int_map_resp_v[NORTH_PORT]), 142 | .fout_req_o (ext_req_v_o[NORTH_PORT]), 143 | .fout_resp_i (ext_resp_v_i[NORTH_PORT]) 144 | ); 145 | 146 | output_module u_output_south ( 147 | .clk (clk), 148 | .arst (arst), 149 | .fin_req_i (int_map_req_v[SOUTH_PORT]), 150 | .fin_resp_o (int_map_resp_v[SOUTH_PORT]), 151 | .fout_req_o (ext_req_v_o[SOUTH_PORT]), 152 | .fout_resp_i (ext_resp_v_i[SOUTH_PORT]) 153 | ); 154 | 155 | output_module u_output_west ( 156 | .clk (clk), 157 | .arst (arst), 158 | .fin_req_i (int_map_req_v[WEST_PORT]), 159 | .fin_resp_o (int_map_resp_v[WEST_PORT]), 160 | .fout_req_o (ext_req_v_o[WEST_PORT]), 161 | .fout_resp_i (ext_resp_v_i[WEST_PORT]) 162 | ); 163 | 164 | output_module u_output_east ( 165 | .clk (clk), 166 | .arst (arst), 167 | .fin_req_i (int_map_req_v[EAST_PORT]), 168 | .fin_resp_o (int_map_resp_v[EAST_PORT]), 169 | .fout_req_o (ext_req_v_o[EAST_PORT]), 170 | .fout_resp_i (ext_resp_v_i[EAST_PORT]) 171 | ); 172 | 173 | output_module u_output_local ( 174 | .clk (clk), 175 | .arst (arst), 176 | .fin_req_i (int_map_req_v[LOCAL_PORT]), 177 | .fin_resp_o (int_map_resp_v[LOCAL_PORT]), 178 | .fout_req_o (ext_req_v_o[LOCAL_PORT]), 179 | .fout_resp_i (ext_resp_v_i[LOCAL_PORT]) 180 | ); 181 | 182 | always_comb begin : mapping_input_ports 183 | ext_req_v_i[NORTH_PORT] = north_recv.req; 184 | north_recv.resp = ext_resp_v_o[NORTH_PORT]; 185 | ext_req_v_i[SOUTH_PORT] = south_recv.req; 186 | south_recv.resp = ext_resp_v_o[SOUTH_PORT]; 187 | ext_req_v_i[WEST_PORT] = west_recv.req; 188 | west_recv.resp = ext_resp_v_o[WEST_PORT]; 189 | ext_req_v_i[EAST_PORT] = east_recv.req; 190 | east_recv.resp = ext_resp_v_o[EAST_PORT]; 191 | 192 | north_send.req = ext_req_v_o[NORTH_PORT]; 193 | ext_resp_v_i[NORTH_PORT] = north_send.resp; 194 | south_send.req = ext_req_v_o[SOUTH_PORT]; 195 | ext_resp_v_i[SOUTH_PORT] = south_send.resp; 196 | west_send.req = ext_req_v_o[WEST_PORT]; 197 | ext_resp_v_i[WEST_PORT] = west_send.resp; 198 | east_send.req = ext_req_v_o[EAST_PORT]; 199 | ext_resp_v_i[EAST_PORT] = east_send.resp; 200 | 201 | // Local interface 202 | local_recv.resp = ext_resp_v_o[LOCAL_PORT]; 203 | ext_req_v_i[LOCAL_PORT] = local_recv.req; 204 | local_send.req = ext_req_v_o[LOCAL_PORT]; 205 | ext_resp_v_i[LOCAL_PORT] = local_send.resp; 206 | end 207 | 208 | always_comb begin : mapping_output_ports 209 | int_resp_v = '0; 210 | int_map_req_v = '0; 211 | 212 | // NORTH - Output module flit in / flit out 213 | int_map_req_v[NORTH_PORT] = {int_route_v[SOUTH_PORT].north_req ? int_req_v[SOUTH_PORT] : '0, 214 | int_route_v[WEST_PORT].north_req ? int_req_v[WEST_PORT] : '0, 215 | int_route_v[EAST_PORT].north_req ? int_req_v[EAST_PORT] : '0, 216 | int_route_v[LOCAL_PORT].north_req ? int_req_v[LOCAL_PORT] : '0}; 217 | 218 | if (int_route_v[SOUTH_PORT].north_req) 219 | int_resp_v[SOUTH_PORT] = int_map_resp_v[NORTH_PORT][3]; 220 | if (int_route_v[WEST_PORT].north_req) 221 | int_resp_v[WEST_PORT] = int_map_resp_v[NORTH_PORT][2]; 222 | if (int_route_v[EAST_PORT].north_req) 223 | int_resp_v[EAST_PORT] = int_map_resp_v[NORTH_PORT][1]; 224 | if (int_route_v[LOCAL_PORT].north_req) 225 | int_resp_v[LOCAL_PORT] = int_map_resp_v[NORTH_PORT][0]; 226 | 227 | // SOUTH - Output module flit in / flit out 228 | int_map_req_v[SOUTH_PORT] = {int_route_v[WEST_PORT].south_req ? int_req_v[WEST_PORT] : '0, 229 | int_route_v[EAST_PORT].south_req ? int_req_v[EAST_PORT] : '0, 230 | int_route_v[LOCAL_PORT].south_req ? int_req_v[LOCAL_PORT] : '0, 231 | int_route_v[NORTH_PORT].south_req ? int_req_v[NORTH_PORT] : '0}; 232 | 233 | if (int_route_v[WEST_PORT].south_req) 234 | int_resp_v[WEST_PORT] = int_map_resp_v[SOUTH_PORT][3]; 235 | if (int_route_v[EAST_PORT].south_req) 236 | int_resp_v[EAST_PORT] = int_map_resp_v[SOUTH_PORT][2]; 237 | if (int_route_v[LOCAL_PORT].south_req) 238 | int_resp_v[LOCAL_PORT] = int_map_resp_v[SOUTH_PORT][1]; 239 | if (int_route_v[NORTH_PORT].south_req) 240 | int_resp_v[NORTH_PORT] = int_map_resp_v[SOUTH_PORT][0]; 241 | 242 | // WEST - Output module flit in / flit out 243 | int_map_req_v[WEST_PORT] = {int_route_v[EAST_PORT].west_req ? int_req_v[EAST_PORT] : '0, 244 | int_route_v[LOCAL_PORT].west_req ? int_req_v[LOCAL_PORT] : '0, 245 | int_route_v[NORTH_PORT].west_req ? int_req_v[NORTH_PORT] : '0, 246 | int_route_v[SOUTH_PORT].west_req ? int_req_v[SOUTH_PORT] : '0}; 247 | 248 | if (int_route_v[EAST_PORT].west_req) 249 | int_resp_v[EAST_PORT] = int_map_resp_v[WEST_PORT][3]; 250 | if (int_route_v[LOCAL_PORT].west_req) 251 | int_resp_v[LOCAL_PORT] = int_map_resp_v[WEST_PORT][2]; 252 | if (int_route_v[NORTH_PORT].west_req) 253 | int_resp_v[NORTH_PORT] = int_map_resp_v[WEST_PORT][1]; 254 | if (int_route_v[SOUTH_PORT].west_req) 255 | int_resp_v[SOUTH_PORT] = int_map_resp_v[WEST_PORT][0]; 256 | 257 | // EAST - Output module flit in / flit out 258 | int_map_req_v[EAST_PORT] = {int_route_v[LOCAL_PORT].east_req ? int_req_v[LOCAL_PORT] : '0, 259 | int_route_v[NORTH_PORT].east_req ? int_req_v[NORTH_PORT] : '0, 260 | int_route_v[SOUTH_PORT].east_req ? int_req_v[SOUTH_PORT] : '0, 261 | int_route_v[WEST_PORT].east_req ? int_req_v[WEST_PORT] : '0}; 262 | 263 | if (int_route_v[LOCAL_PORT].east_req) 264 | int_resp_v[LOCAL_PORT] = int_map_resp_v[EAST_PORT][3]; 265 | if (int_route_v[NORTH_PORT].east_req) 266 | int_resp_v[NORTH_PORT] = int_map_resp_v[EAST_PORT][2]; 267 | if (int_route_v[SOUTH_PORT].east_req) 268 | int_resp_v[SOUTH_PORT] = int_map_resp_v[EAST_PORT][1]; 269 | if (int_route_v[WEST_PORT].east_req) 270 | int_resp_v[WEST_PORT] = int_map_resp_v[EAST_PORT][0]; 271 | 272 | // LOCAL - Output module flit in / flit out 273 | int_map_req_v[LOCAL_PORT] = {int_route_v[NORTH_PORT].local_req ? int_req_v[NORTH_PORT] : '0, 274 | int_route_v[SOUTH_PORT].local_req ? int_req_v[SOUTH_PORT] : '0, 275 | int_route_v[WEST_PORT].local_req ? int_req_v[WEST_PORT] : '0, 276 | int_route_v[EAST_PORT].local_req ? int_req_v[EAST_PORT] : '0}; 277 | 278 | if (int_route_v[NORTH_PORT].local_req) 279 | int_resp_v[NORTH_PORT] = int_map_resp_v[LOCAL_PORT][3]; 280 | if (int_route_v[SOUTH_PORT].local_req) 281 | int_resp_v[SOUTH_PORT] = int_map_resp_v[LOCAL_PORT][2]; 282 | if (int_route_v[WEST_PORT].local_req) 283 | int_resp_v[WEST_PORT] = int_map_resp_v[LOCAL_PORT][1]; 284 | if (int_route_v[EAST_PORT].local_req) 285 | int_resp_v[EAST_PORT] = int_map_resp_v[LOCAL_PORT][0]; 286 | end 287 | 288 | endmodule 289 | 290 | // Code commented works but bc of generate it's hard to know 291 | // which module is the direction, that's why it's manually 292 | // instantiated again 293 | //genvar in_mod; 294 | //generate 295 | //for(in_mod=0;in_mod<5;in_mod++) begin : input_modules 296 | //input_module # ( 297 | //.ROUTER_X_ID(ROUTER_X_ID), 298 | //.ROUTER_Y_ID(ROUTER_Y_ID) 299 | //) u_input_module ( 300 | //.clk (clk), 301 | //.arst (arst), 302 | //.fin_req_i (ext_req_v_i[in_mod]), 303 | //.fin_resp_o (ext_resp_v_o[in_mod]), 304 | //.fout_req_o (int_req_v[in_mod]), 305 | //.fout_resp_i (int_resp_v[in_mod]), 306 | //.router_port_o(int_route_v[in_mod]) 307 | //); 308 | //end 309 | //endgenerate 310 | 311 | //genvar out_mod; 312 | //generate 313 | //for(out_mod=0;out_mod<5;out_mod++) begin : output_modules 314 | //output_module u_output_module ( 315 | //.clk(clk), 316 | //.arst(arst), 317 | //.fin_req_i(int_map_req_v[out_mod]), 318 | //.fin_resp_o(int_map_resp_v[out_mod]), 319 | //.fout_req_o(ext_req_v_o[out_mod]), 320 | //.fout_resp_i(ext_resp_v_i[out_mod]) 321 | //); 322 | //end 323 | //endgenerate 324 | -------------------------------------------------------------------------------- /src/ravenoc_wrapper.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * File: RaveNoC wrapper module 3 | * Description: It only exists because it facilitates intg. with cocotb 4 | * Author: Anderson Ignacio da Silva 5 | * 6 | * MIT License 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | module ravenoc_wrapper 26 | import amba_axi_pkg::*; 27 | import ravenoc_pkg::*; 28 | #( 29 | parameter bit DEBUG = 0 30 | )( 31 | /*verilator coverage_off*/ 32 | input clk_axi, 33 | input clk_noc, 34 | input arst_axi, 35 | input arst_noc, 36 | // AXI mux I/F 37 | input act_in, 38 | input [$clog2(NoCSize)-1:0] axi_sel_in, 39 | input act_out, 40 | input [$clog2(NoCSize)-1:0] axi_sel_out, 41 | // Used to test when clk_axi == clk_noc to bypass CDC 42 | input bypass_cdc, 43 | // AXI in I/F 44 | // AXI Interface - MOSI 45 | // Write Address channel 46 | input logic noc_in_awid, 47 | input axi_addr_t noc_in_awaddr, 48 | input logic [`AXI_ALEN_WIDTH-1:0] noc_in_awlen, 49 | input axi_size_t noc_in_awsize, 50 | input axi_burst_t noc_in_awburst, 51 | input logic noc_in_awlock, 52 | input logic [3:0] noc_in_awcache, 53 | input logic [2:0] noc_in_awprot, 54 | input logic [3:0] noc_in_awqos, 55 | input logic [3:0] noc_in_awregion, 56 | input logic [`AXI_USER_REQ_WIDTH-1:0] noc_in_awuser, 57 | input logic noc_in_awvalid, 58 | // Write Data channel 59 | //input logic noc_in_wid, 60 | input logic [`AXI_DATA_WIDTH-1:0] noc_in_wdata, 61 | input logic [(`AXI_DATA_WIDTH/8)-1:0] noc_in_wstrb, 62 | input logic noc_in_wlast, 63 | input logic [`AXI_USER_DATA_WIDTH-1:0] noc_in_wuser, 64 | input logic noc_in_wvalid, 65 | // Write Response channel 66 | input logic noc_in_bready, 67 | // Read Address channel 68 | input logic noc_in_arid, 69 | input axi_addr_t noc_in_araddr, 70 | input logic [`AXI_ALEN_WIDTH-1:0] noc_in_arlen, 71 | input axi_size_t noc_in_arsize, 72 | input axi_burst_t noc_in_arburst, 73 | input logic noc_in_arlock, 74 | input logic [3:0] noc_in_arcache, 75 | input logic [2:0] noc_in_arprot, 76 | input logic [3:0] noc_in_arqos, 77 | input logic [3:0] noc_in_arregion, 78 | input logic [`AXI_USER_REQ_WIDTH-1:0] noc_in_aruser, 79 | input logic noc_in_arvalid, 80 | // Read Data channel 81 | input logic noc_in_rready, 82 | 83 | // AXI Interface - MISO 84 | // Write Addr channel 85 | output logic noc_in_awready, 86 | // Write Data channel 87 | output logic noc_in_wready, 88 | // Write Response channel 89 | output logic noc_in_bid, 90 | output axi_resp_t noc_in_bresp, 91 | output logic [`AXI_USER_RESP_WIDTH-1:0] noc_in_buser, 92 | output logic noc_in_bvalid, 93 | // Read addr channel 94 | output logic noc_in_arready, 95 | // Read data channel 96 | output logic noc_in_rid, 97 | output logic [`AXI_DATA_WIDTH-1:0] noc_in_rdata, 98 | output axi_resp_t noc_in_rresp, 99 | output logic noc_in_rlast, 100 | output logic [`AXI_USER_REQ_WIDTH-1:0] noc_in_ruser, 101 | output logic noc_in_rvalid, 102 | 103 | // AXI out I/F 104 | // AXI Interface - MOSI 105 | // Write Address channel 106 | input logic noc_out_awid, 107 | input axi_addr_t noc_out_awaddr, 108 | input logic [`AXI_ALEN_WIDTH-1:0] noc_out_awlen, 109 | input axi_size_t noc_out_awsize, 110 | input axi_burst_t noc_out_awburst, 111 | input logic noc_out_awlock, 112 | input logic [3:0] noc_out_awcache, 113 | input logic [2:0] noc_out_awprot, 114 | input logic [3:0] noc_out_awqos, 115 | input logic [3:0] noc_out_awregion, 116 | input logic [`AXI_USER_REQ_WIDTH-1:0] noc_out_awuser, 117 | input logic noc_out_awvalid, 118 | // Write Data channel 119 | //input logic noc_out_wid, 120 | input logic [`AXI_DATA_WIDTH-1:0] noc_out_wdata, 121 | input logic [(`AXI_DATA_WIDTH/8)-1:0] noc_out_wstrb, 122 | input logic noc_out_wlast, 123 | input logic [`AXI_USER_DATA_WIDTH-1:0] noc_out_wuser, 124 | input logic noc_out_wvalid, 125 | // Write Response channel 126 | input logic noc_out_bready, 127 | // Read Address channel 128 | input logic noc_out_arid, 129 | input axi_addr_t noc_out_araddr, 130 | input logic [`AXI_ALEN_WIDTH-1:0] noc_out_arlen, 131 | input axi_size_t noc_out_arsize, 132 | input axi_burst_t noc_out_arburst, 133 | input logic noc_out_arlock, 134 | input logic [3:0] noc_out_arcache, 135 | input logic [2:0] noc_out_arprot, 136 | input logic [3:0] noc_out_arqos, 137 | input logic [3:0] noc_out_arregion, 138 | input logic [`AXI_USER_REQ_WIDTH-1:0] noc_out_aruser, 139 | input logic noc_out_arvalid, 140 | // Read Data channel 141 | input logic noc_out_rready, 142 | // AXI Interface - MISO 143 | // Write Addr channel 144 | output logic noc_out_awready, 145 | // Write Data channel 146 | output logic noc_out_wready, 147 | // Write Response channel 148 | output logic noc_out_bid, 149 | output axi_resp_t noc_out_bresp, 150 | output logic [`AXI_USER_RESP_WIDTH-1:0] noc_out_buser, 151 | output logic noc_out_bvalid, 152 | // Read addr channel 153 | output logic noc_out_arready, 154 | // Read data channel 155 | output logic noc_out_rid, 156 | output logic [`AXI_DATA_WIDTH-1:0] noc_out_rdata, 157 | output axi_resp_t noc_out_rresp, 158 | output logic noc_out_rlast, 159 | output logic [`AXI_USER_REQ_WIDTH-1:0] noc_out_ruser, 160 | output logic noc_out_rvalid, 161 | // IRQs 162 | output logic [NumVirtChn*NoCSize-1:0] irqs_out 163 | ); 164 | s_axi_mosi_t [NoCSize-1:0] axi_mosi; 165 | s_axi_miso_t [NoCSize-1:0] axi_miso; 166 | s_irq_ni_t [NoCSize-1:0] irqs; 167 | logic [NoCSize-1:0] bypass_cdc_vec; 168 | logic [NoCSize-1:0] clk_axi_array; 169 | logic [NoCSize-1:0] arst_axi_array; 170 | 171 | always begin 172 | for (int i=0;i (axi_sel_in != axi_sel_out) 332 | ) else $error("Illegal mux on the AXI!"); 333 | 334 | endmodule 335 | -------------------------------------------------------------------------------- /tb/common_noc/testbench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File : testbench.py 4 | # License : MIT license 5 | # Author : Anderson Ignacio da Silva (aignacio) 6 | # Date : 01.03.2021 7 | # Last Modified Date: 31.03.2021 8 | # Last Modified By : Anderson Ignacio da Silva (aignacio) 9 | import cocotb 10 | import os, errno 11 | import logging, string, random 12 | from logging.handlers import RotatingFileHandler 13 | from cocotb.log import SimLogFormatter, SimColourLogFormatter, SimLog, SimTimeContextFilter 14 | from common_noc.constants import noc_const 15 | from cocotb.clock import Clock 16 | from datetime import datetime 17 | from cocotb.triggers import ClockCycles, RisingEdge, with_timeout, ReadOnly, Event 18 | from common_noc.ravenoc_pkt import RaveNoC_pkt 19 | from cocotbext.axi import AxiBus, AxiMaster, AxiRam, AxiResp 20 | from cocotb.result import TestFailure 21 | 22 | class Tb: 23 | """ 24 | Base class for RaveNoC testbench 25 | 26 | Args: 27 | dut: The Dut object coming from cocotb 28 | log_name: Name of the log file inside the run folder, it's append the timestamp only 29 | cfg: NoC cfg dict 30 | """ 31 | def __init__(self, dut, log_name, cfg): 32 | self.dut = dut 33 | self.cfg = cfg 34 | timenow_wstamp = self._gen_log(log_name) 35 | self.log.info("------------[LOG - %s]------------",timenow_wstamp) 36 | self.log.info("SEED: %s",str(cocotb.RANDOM_SEED)) 37 | self.log.info("Log file: %s",log_name) 38 | self._print_noc_cfg() 39 | # Create the AXI Master I/Fs and connect it to the two main AXI Slave I/Fs in the top wrappers 40 | self.noc_axi_in = AxiMaster(AxiBus.from_prefix(self.dut, "noc_in"), self.dut.clk_axi, self.dut.arst_axi) 41 | self.noc_axi_out = AxiMaster(AxiBus.from_prefix(self.dut, "noc_out"), self.dut.clk_axi, self.dut.arst_axi) 42 | # Tied to zero the inputs 43 | self.dut.act_in.setimmediatevalue(0) 44 | self.dut.act_out.setimmediatevalue(0) 45 | self.dut.axi_sel_in.setimmediatevalue(0) 46 | self.dut.axi_sel_out.setimmediatevalue(0) 47 | 48 | def __del__(self): 49 | # Need to write the last strings in the buffer in the file 50 | self.log.info("Closing log file.") 51 | self.log.removeHandler(self.file_handler) 52 | 53 | def set_idle_generator(self, generator=None): 54 | if generator: 55 | self.noc_axi_in.write_if.aw_channel.set_pause_generator(generator()) 56 | self.noc_axi_in.write_if.w_channel.set_pause_generator(generator()) 57 | self.noc_axi_in.read_if.ar_channel.set_pause_generator(generator()) 58 | self.noc_axi_out.write_if.aw_channel.set_pause_generator(generator()) 59 | self.noc_axi_out.write_if.w_channel.set_pause_generator(generator()) 60 | self.noc_axi_out.read_if.ar_channel.set_pause_generator(generator()) 61 | 62 | def set_backpressure_generator(self, generator=None): 63 | if generator: 64 | self.noc_axi_in.write_if.b_channel.set_pause_generator(generator()) 65 | self.noc_axi_in.read_if.r_channel.set_pause_generator(generator()) 66 | self.noc_axi_out.write_if.b_channel.set_pause_generator(generator()) 67 | self.noc_axi_out.read_if.r_channel.set_pause_generator(generator()) 68 | 69 | """ 70 | Write method to transfer pkts over the NoC 71 | 72 | Args: 73 | pkt: Input pkt to be transfered to the NoC 74 | kwargs: All aditional args that can be passed to the amba AXI driver 75 | """ 76 | async def write_pkt(self, pkt=RaveNoC_pkt, timeout=noc_const.TIMEOUT_AXI, use_side_if=0, **kwargs): 77 | if use_side_if == 0: 78 | self.dut.axi_sel_in.setimmediatevalue(pkt.src[0]) 79 | self.dut.act_in.setimmediatevalue(1) 80 | else: 81 | self.dut.axi_sel_out.setimmediatevalue(pkt.src[0]) 82 | self.dut.act_out.setimmediatevalue(1) 83 | 84 | self._print_pkt_header("write",pkt) 85 | #self.log.info("[AXI Master - Write NoC Packet] Data:") 86 | #self._print_pkt(pkt.message, pkt.num_bytes_per_beat) 87 | if use_side_if == 0: 88 | write = self.noc_axi_in.init_write(address=pkt.axi_address_w, awid=0x0, data=pkt.msg, **kwargs) 89 | else: 90 | write = self.noc_axi_out.init_write(address=pkt.axi_address_w, awid=0x0, data=pkt.msg, **kwargs) 91 | 92 | await with_timeout(write.wait(), *timeout) 93 | ret = write.data 94 | 95 | if use_side_if == 0: 96 | self.dut.act_in.setimmediatevalue(0) 97 | self.dut.axi_sel_in.setimmediatevalue(0) 98 | else: 99 | self.dut.act_out.setimmediatevalue(0) 100 | self.dut.axi_sel_out.setimmediatevalue(0) 101 | return ret 102 | 103 | """ 104 | Read method to fetch pkts from the NoC 105 | 106 | Args: 107 | pkt: Valid pkt to be used as inputs args (vc_channel, node on the axi_mux input,..) to the read op from the NoC 108 | kwargs: All aditional args that can be passed to the amba AXI driver 109 | Returns: 110 | Return the packet message with the head flit 111 | """ 112 | async def read_pkt(self, pkt=RaveNoC_pkt, timeout=noc_const.TIMEOUT_AXI, **kwargs): 113 | self.dut.axi_sel_out.setimmediatevalue(pkt.dest[0]) 114 | self.dut.act_out.setimmediatevalue(1) 115 | self._print_pkt_header("read",pkt) 116 | read = self.noc_axi_out.init_read(address=pkt.axi_address_r, arid=0x0, length=pkt.length, **kwargs) 117 | await with_timeout(read.wait(), *timeout) 118 | ret = read.data # read.data => AxiReadResp 119 | # self.log.info("[AXI Master - Read NoC Packet] Data:") 120 | #self._print_pkt(ret.data, pkt.num_bytes_per_beat) 121 | self.dut.act_out.setimmediatevalue(0) 122 | self.dut.axi_sel_out.setimmediatevalue(0) 123 | return ret 124 | 125 | """ 126 | Write AXI method 127 | 128 | Args: 129 | sel: axi_mux switch to select the correct node to write through 130 | kwargs: All aditional args that can be passed to the amba AXI driver 131 | """ 132 | async def write(self, sel=0, address=0x0, data=0x0, **kwargs): 133 | self.dut.act_in.setimmediatevalue(1) 134 | self.dut.axi_sel_in.setimmediatevalue(sel) 135 | self.log.info("[AXI Master - Write] Slave = ["+str(sel)+"] / Address = ["+str(hex(address))+"] ") 136 | write = self.noc_axi_in.init_write(address=address, awid=0x0, data=data, **kwargs) 137 | await with_timeout(write.wait(), *noc_const.TIMEOUT_AXI) 138 | ret = write.data 139 | self.dut.axi_sel_in.setimmediatevalue(0) 140 | self.dut.act_in.setimmediatevalue(0) 141 | return ret 142 | 143 | """ 144 | Read AXI method 145 | 146 | Args: 147 | sel: axi_mux switch to select the correct node to read from 148 | kwargs: All aditional args that can be passed to the amba AXI driver 149 | Returns: 150 | Return the data read from the specified node 151 | """ 152 | async def read(self, sel=0, address=0x0, length=4, **kwargs): 153 | self.dut.act_out.setimmediatevalue(1) 154 | self.dut.axi_sel_out.setimmediatevalue(sel) 155 | self.log.info("[AXI Master - Read] Slave = ["+str(sel)+"] / Address = ["+str(hex(address))+"] / Length = ["+str(length)+" bytes]") 156 | read = self.noc_axi_out.init_read(address=address, arid=0x0, length=length, **kwargs) 157 | await with_timeout(read.wait(), *noc_const.TIMEOUT_AXI) 158 | resp = read.data # read.data => AxiReadResp 159 | self.dut.axi_sel_out.setimmediatevalue(0) 160 | self.dut.act_out.setimmediatevalue(0) 161 | return resp 162 | 163 | """ 164 | Auxiliary method to check received data 165 | """ 166 | def check_pkt(self, data, received): 167 | assert len(data) == len(received), "Lengths are different from received to sent pkt" 168 | for i in range(len(data)): 169 | assert data[i] == received[i], "Mismatch on received vs sent NoC packet!" 170 | 171 | """ 172 | Auxiliary method to log flit header 173 | """ 174 | def _print_pkt_header(self, op, pkt): 175 | axi_addr = str(hex(pkt.axi_address_r)) if op=="read" else str(hex(pkt.axi_address_w)) 176 | mux = str(pkt.dest[0]) if op=="read" else str(pkt.src[0]) 177 | self.log.info(f"[AXI Master - "+str(op)+" NoC Packet] Router=["+mux+"] " 178 | "Address=[AXI_Addr="+axi_addr+"] Mux_"+op+"=["+mux+"] " 179 | "SRC(x,y)=["+str(pkt.src[1])+","+str(pkt.src[2])+"] " 180 | "DEST(x,y)=["+str(pkt.dest[1])+","+str(pkt.dest[2])+"] " 181 | "Length=["+str(pkt.length)+" bytes / "+str(pkt.length_beats)+" beats]") 182 | 183 | """ 184 | Auxiliary method to print/log AXI payload 185 | """ 186 | def _print_pkt(self, data, bytes_per_beat): 187 | print("LEN="+str(len(data))+" BYTES PER BEAT="+str(bytes_per_beat)) 188 | if len(data) == bytes_per_beat: 189 | beat_burst_hex = [data[x] for x in range(0,bytes_per_beat)][::-1] 190 | beat_burst_hs = "" 191 | for j in beat_burst_hex: 192 | beat_burst_hs += hex(j) 193 | beat_burst_hs += "\t("+chr(j)+")" 194 | beat_burst_hs += "\t" 195 | tmp = "Beat[0]---> "+beat_burst_hs 196 | self.log.info(tmp) 197 | else: 198 | for i in range(0,len(data),bytes_per_beat): 199 | beat_burst_hex = [data[x] for x in range(i,i+bytes_per_beat)][::-1] 200 | # beat_burst_s = [chr(data[x]) for x in range(i,i+bytes_per_beat)][::-1] 201 | beat_burst_hs = "" 202 | for j in beat_burst_hex: 203 | beat_burst_hs += hex(j) 204 | beat_burst_hs += "\t("+chr(j)+")" 205 | beat_burst_hs += "\t" 206 | tmp = "Beat["+str(int(i/bytes_per_beat))+"]---> "+beat_burst_hs 207 | self.log.info(tmp) 208 | #print("--) 209 | 210 | """ 211 | Setup and launch the clocks on the simulation 212 | 213 | Args: 214 | clk_mode: Selects between AXI clk higher than NoC clk and vice-versa 215 | """ 216 | async def setup_clks(self, clk_mode="NoC_slwT_AXI"): 217 | self.log.info(f"[Setup] Configuring the clocks: {clk_mode}") 218 | if clk_mode == "NoC_slwT_AXI": 219 | cocotb.fork(Clock(self.dut.clk_noc, *noc_const.CLK_100MHz).start()) 220 | cocotb.fork(Clock(self.dut.clk_axi, *noc_const.CLK_200MHz).start()) 221 | elif clk_mode == "AXI_slwT_NoC": 222 | cocotb.fork(Clock(self.dut.clk_axi, *noc_const.CLK_100MHz).start()) 223 | cocotb.fork(Clock(self.dut.clk_noc, *noc_const.CLK_200MHz).start()) 224 | else: 225 | cocotb.fork(Clock(self.dut.clk_axi, *noc_const.CLK_200MHz).start()) 226 | cocotb.fork(Clock(self.dut.clk_noc, *noc_const.CLK_200MHz).start()) 227 | 228 | 229 | """ 230 | Setup and apply the reset on the NoC 231 | 232 | Args: 233 | clk_mode: Depending on the input clock mode, we need to wait different 234 | clk cycles for the reset, we always hold as long as the slowest clock 235 | """ 236 | async def arst(self, clk_mode="NoC_slwT_AXI"): 237 | self.log.info("[Setup] Reset DUT") 238 | self.dut.arst_axi.setimmediatevalue(0) 239 | self.dut.arst_noc.setimmediatevalue(0) 240 | self.dut.axi_sel_in.setimmediatevalue(0) 241 | self.dut.axi_sel_out.setimmediatevalue(0) 242 | self.dut.act_in.setimmediatevalue(0) 243 | self.dut.act_out.setimmediatevalue(0) 244 | bypass_cdc = 1 if clk_mode == "NoC_equal_AXI" else 0 245 | self.dut.bypass_cdc.setimmediatevalue(bypass_cdc) 246 | self.dut.arst_axi <= 1 247 | self.dut.arst_noc <= 1 248 | # await NextTimeStep() 249 | #await ReadOnly() #https://github.com/cocotb/cocotb/issues/2478 250 | if clk_mode == "NoC_slwT_AXI": 251 | await ClockCycles(self.dut.clk_noc, noc_const.RST_CYCLES) 252 | else: 253 | await ClockCycles(self.dut.clk_axi, noc_const.RST_CYCLES) 254 | self.dut.arst_axi <= 0 255 | self.dut.arst_noc <= 0 256 | # https://github.com/alexforencich/cocotbext-axi/issues/19 257 | #await ClockCycles(self.dut.clk_axi, 1) 258 | #await ClockCycles(self.dut.clk_noc, 1) 259 | 260 | """ 261 | Method to wait for IRQs from the NoC 262 | """ 263 | async def wait_irq(self): 264 | # We need to wait some clock cyles because the in/out axi I/F is muxed 265 | # once verilator 4.106 doesn't support array of structs in the top. 266 | # This trigger is required because we read much faster than we write 267 | # and if we don't wait for the flit to arrive, it'll throw an error of 268 | # empty rd buffer 269 | # if tb.dut.irqs_out.value.integer == 0: 270 | #await with_timeout(First(*(Edge(bit) for bit in tb.dut.irqs_out)), *noc_const.TIMEOUT_IRQ) 271 | # This only exists bc of this: 272 | # https://github.com/cocotb/cocotb/issues/2478 273 | timeout_cnt = 0 274 | while int(self.dut.irqs_out) == 0: 275 | await RisingEdge(self.dut.clk_noc) 276 | if timeout_cnt == noc_const.TIMEOUT_IRQ_V: 277 | self.log.error("Timeout on waiting for an IRQ") 278 | raise TestFailure("Timeout on waiting for an IRQ") 279 | else: 280 | timeout_cnt += 1 281 | 282 | """ 283 | Method to wait for IRQs from the NoC with a specific value 284 | """ 285 | async def wait_irq_x(self, val): 286 | # We need to wait some clock cyles because the in/out axi I/F is muxed 287 | # once verilator 4.106 doesn't support array of structs in the top. 288 | # This trigger is required because we read much faster than we write 289 | # and if we don't wait for the flit to arrive, it'll throw an error of 290 | # empty rd buffer 291 | # if tb.dut.irqs_out.value.integer == 0: 292 | #await with_timeout(First(*(Edge(bit) for bit in tb.dut.irqs_out)), *noc_const.TIMEOUT_IRQ) 293 | # This only exists bc of this: 294 | # https://github.com/cocotb/cocotb/issues/2478 295 | timeout_cnt = 0 296 | while int(self.dut.irqs_out) != val: 297 | await RisingEdge(self.dut.clk_noc) 298 | if timeout_cnt == noc_const.TIMEOUT_IRQ_V: 299 | self.log.error("Timeout on waiting for an IRQ") 300 | raise TestFailure("Timeout on waiting for an IRQ") 301 | else: 302 | timeout_cnt += 1 303 | 304 | """ 305 | Creates the tb log obj and start filling with headers 306 | """ 307 | def _gen_log(self, log_name): 308 | timenow = datetime.now().strftime("%d_%b_%Y_%Hh_%Mm_%Ss") 309 | timenow_wstamp = timenow + str("_") + str(datetime.timestamp(datetime.now())) 310 | self.log = SimLog(log_name) 311 | self.log.setLevel(logging.DEBUG) 312 | self.file_handler = RotatingFileHandler(f"{log_name}_{timenow}.log", maxBytes=(5 * 1024 * 1024), backupCount=2, mode='w') 313 | self._symlink_force(f"{log_name}_{timenow}.log",f"latest_{log_name}.log") 314 | self.file_handler.setFormatter(SimLogFormatter()) 315 | self.log.addHandler(self.file_handler) 316 | self.log.addFilter(SimTimeContextFilter()) 317 | return timenow_wstamp 318 | 319 | """ 320 | Used to create the symlink with the latest log in the run dir folder 321 | """ 322 | def _symlink_force(self, target, link_name): 323 | try: 324 | os.symlink(target, link_name) 325 | except OSError as e: 326 | if e.errno == errno.EEXIST: 327 | os.remove(link_name) 328 | os.symlink(target, link_name) 329 | else: 330 | raise e 331 | 332 | """ 333 | Returns a random string with the length equal to input argument 334 | """ 335 | def _get_random_string(self, length=1): 336 | # choose from all lowercase letter 337 | letters = string.ascii_lowercase 338 | result_str = ''.join(random.choice(letters) for i in range(length)) 339 | return result_str 340 | 341 | def _print_noc_cfg(self): 342 | cfg = self.cfg 343 | 344 | self.log.info("------------------------------") 345 | self.log.info("RaveNoC configuration:") 346 | self.log.info(f"--> Flit data width: "+str(cfg['flit_data_width'])) 347 | self.log.info(f"--> AXI data width: "+str(cfg['flit_data_width'])) 348 | self.log.info(f"--> Routing algorithm: "+cfg['routing_alg']) 349 | self.log.info(f"--> NoC Size: "+str(cfg['noc_cfg_sz_rows'])+"x"+str(cfg['noc_cfg_sz_cols'])) 350 | self.log.info(f"--> Number of flit buffers per input module: "+str(cfg['flit_buff'])) 351 | self.log.info(f"--> Max size per pkt (beats): "+str(cfg['max_sz_pkt'])) 352 | self.log.info(f"--> Number of virtual channels: "+str(cfg['n_virt_chn'])) 353 | self.log.info(f"--> VC ID priority: "+("VC[0] has highest priority" if cfg['h_priority'] == 1 else "VC[0] has lowest priority")) 354 | self.log.info("------------------------------") 355 | --------------------------------------------------------------------------------