diff --git a/Makefile b/Makefile index f65c4640b..cacc6c672 100644 --- a/Makefile +++ b/Makefile @@ -159,6 +159,7 @@ app: clean-app echo "\033[0;31mI would start by checking b) if I were you!\033[0m"; \ exit 1; \ } + python post_proc_hex.py sw/build/main.hex sw/build/main.hex ## Just list the different application names available app-list: diff --git a/core-v-mini-mcu.core b/core-v-mini-mcu.core index aef1f20e2..a32a61295 100644 --- a/core-v-mini-mcu.core +++ b/core-v-mini-mcu.core @@ -20,6 +20,8 @@ filesets: - pulp-platform.org::cluster_interconnect - pulp-platform.org::riscv_dbg - pulp-platform.org::register_interface + - pulp-platform.org::hyperbus + - pulp-platform.org::axi_heep - openhwgroup.org:ip:soc_ctrl - lowrisc:ip:uart:0.1 - lowrisc:ip:rv_plic_example:0.1 @@ -48,6 +50,7 @@ filesets: - hw/core-v-mini-mcu/debug_subsystem.sv - hw/core-v-mini-mcu/peripheral_subsystem.sv - hw/core-v-mini-mcu/ao_peripheral_subsystem.sv + - hw/core-v-mini-mcu/hyperbus_subsystem.sv file_type: systemVerilogSource rtl-simulation: diff --git a/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv b/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv index dd9246dfc..078ef36ff 100644 --- a/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv +++ b/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv @@ -75,6 +75,10 @@ module ao_peripheral_subsystem output reg_req_t pad_req_o, input reg_rsp_t pad_resp_i, + //HyperBus Regs + output reg_req_t hyperbus_req_o, + input reg_rsp_t hyperbus_rsp_i, + // FAST INTR CTRL input logic [14:0] fast_intr_i, output logic [14:0] fast_intr_o, @@ -361,6 +365,9 @@ module ao_peripheral_subsystem assign pad_req_o = ao_peripheral_slv_req[core_v_mini_mcu_pkg::PAD_CONTROL_IDX]; assign ao_peripheral_slv_rsp[core_v_mini_mcu_pkg::PAD_CONTROL_IDX] = pad_resp_i; + assign hyperbus_req_o = ao_peripheral_slv_req[core_v_mini_mcu_pkg::HYPERBUS_IDX]; + assign ao_peripheral_slv_rsp[core_v_mini_mcu_pkg::HYPERBUS_IDX] = hyperbus_rsp_i; + fast_intr_ctrl #( .reg_req_t(reg_pkg::reg_req_t), .reg_rsp_t(reg_pkg::reg_rsp_t) diff --git a/hw/core-v-mini-mcu/core_v_mini_mcu.sv b/hw/core-v-mini-mcu/core_v_mini_mcu.sv index 4cfb3d9d5..2fd27dc8c 100644 --- a/hw/core-v-mini-mcu/core_v_mini_mcu.sv +++ b/hw/core-v-mini-mcu/core_v_mini_mcu.sv @@ -100,22 +100,6 @@ module core_v_mini_mcu input logic gpio_13_i, output logic gpio_13_oe_o, - output logic gpio_14_o, - input logic gpio_14_i, - output logic gpio_14_oe_o, - - output logic gpio_15_o, - input logic gpio_15_i, - output logic gpio_15_oe_o, - - output logic gpio_16_o, - input logic gpio_16_i, - output logic gpio_16_oe_o, - - output logic gpio_17_o, - input logic gpio_17_i, - output logic gpio_17_oe_o, - output logic spi_flash_sck_o, input logic spi_flash_sck_i, output logic spi_flash_sck_oe_o, @@ -172,86 +156,133 @@ module core_v_mini_mcu input logic spi_sd_3_i, output logic spi_sd_3_oe_o, - output logic pdm2pcm_pdm_o, - input logic pdm2pcm_pdm_i, - output logic pdm2pcm_pdm_oe_o, + output logic hyper_cs_no, + output logic gpio_14_o, + input logic gpio_14_i, + output logic gpio_14_oe_o, + + output logic hyper_ck_o, + output logic gpio_15_o, + input logic gpio_15_i, + output logic gpio_15_oe_o, + + output logic hyper_ckn_o, + output logic gpio_16_o, + input logic gpio_16_i, + output logic gpio_16_oe_o, + + output logic hyper_rwds_o, + input logic hyper_rwds_i, + output logic hyper_rwds_oe_o, + output logic gpio_17_o, + input logic gpio_17_i, + output logic gpio_17_oe_o, + + output logic hyper_reset_no, output logic gpio_18_o, input logic gpio_18_i, output logic gpio_18_oe_o, + output logic spi2_cs_0_o, + input logic spi2_cs_0_i, + output logic spi2_cs_0_oe_o, - output logic pdm2pcm_clk_o, - input logic pdm2pcm_clk_i, - output logic pdm2pcm_clk_oe_o, + output logic hyper_dq_0_o, + input logic hyper_dq_0_i, + output logic hyper_dq_0_oe_o, output logic gpio_19_o, input logic gpio_19_i, output logic gpio_19_oe_o, + output logic spi2_sd_0_o, + input logic spi2_sd_0_i, + output logic spi2_sd_0_oe_o, - output logic i2s_sck_o, - input logic i2s_sck_i, - output logic i2s_sck_oe_o, + output logic hyper_dq_1_o, + input logic hyper_dq_1_i, + output logic hyper_dq_1_oe_o, output logic gpio_20_o, input logic gpio_20_i, output logic gpio_20_oe_o, + output logic spi2_sd_1_o, + input logic spi2_sd_1_i, + output logic spi2_sd_1_oe_o, - output logic i2s_ws_o, - input logic i2s_ws_i, - output logic i2s_ws_oe_o, + output logic hyper_dq_2_o, + input logic hyper_dq_2_i, + output logic hyper_dq_2_oe_o, output logic gpio_21_o, input logic gpio_21_i, output logic gpio_21_oe_o, + output logic spi2_sd_2_o, + input logic spi2_sd_2_i, + output logic spi2_sd_2_oe_o, - output logic i2s_sd_o, - input logic i2s_sd_i, - output logic i2s_sd_oe_o, + output logic hyper_dq_3_o, + input logic hyper_dq_3_i, + output logic hyper_dq_3_oe_o, output logic gpio_22_o, input logic gpio_22_i, output logic gpio_22_oe_o, + output logic spi2_sd_3_o, + input logic spi2_sd_3_i, + output logic spi2_sd_3_oe_o, - output logic spi2_cs_0_o, - input logic spi2_cs_0_i, - output logic spi2_cs_0_oe_o, + output logic hyper_dq_4_o, + input logic hyper_dq_4_i, + output logic hyper_dq_4_oe_o, output logic gpio_23_o, input logic gpio_23_i, output logic gpio_23_oe_o, + output logic spi2_sck_o, + input logic spi2_sck_i, + output logic spi2_sck_oe_o, - output logic spi2_cs_1_o, - input logic spi2_cs_1_i, - output logic spi2_cs_1_oe_o, + output logic hyper_dq_5_o, + input logic hyper_dq_5_i, + output logic hyper_dq_5_oe_o, output logic gpio_24_o, input logic gpio_24_i, output logic gpio_24_oe_o, + output logic pdm2pcm_pdm_o, + input logic pdm2pcm_pdm_i, + output logic pdm2pcm_pdm_oe_o, - output logic spi2_sck_o, - input logic spi2_sck_i, - output logic spi2_sck_oe_o, + output logic hyper_dq_6_o, + input logic hyper_dq_6_i, + output logic hyper_dq_6_oe_o, output logic gpio_25_o, input logic gpio_25_i, output logic gpio_25_oe_o, + output logic pdm2pcm_clk_o, + input logic pdm2pcm_clk_i, + output logic pdm2pcm_clk_oe_o, - output logic spi2_sd_0_o, - input logic spi2_sd_0_i, - output logic spi2_sd_0_oe_o, + output logic hyper_dq_7_o, + input logic hyper_dq_7_i, + output logic hyper_dq_7_oe_o, output logic gpio_26_o, input logic gpio_26_i, output logic gpio_26_oe_o, + output logic spi2_cs_1_o, + input logic spi2_cs_1_i, + output logic spi2_cs_1_oe_o, - output logic spi2_sd_1_o, - input logic spi2_sd_1_i, - output logic spi2_sd_1_oe_o, + output logic i2s_sck_o, + input logic i2s_sck_i, + output logic i2s_sck_oe_o, output logic gpio_27_o, input logic gpio_27_i, output logic gpio_27_oe_o, - output logic spi2_sd_2_o, - input logic spi2_sd_2_i, - output logic spi2_sd_2_oe_o, + output logic i2s_ws_o, + input logic i2s_ws_i, + output logic i2s_ws_oe_o, output logic gpio_28_o, input logic gpio_28_i, output logic gpio_28_oe_o, - output logic spi2_sd_3_o, - input logic spi2_sd_3_i, - output logic spi2_sd_3_oe_o, + output logic i2s_sd_o, + input logic i2s_sd_i, + output logic i2s_sd_oe_o, output logic gpio_29_o, input logic gpio_29_i, output logic gpio_29_oe_o, @@ -259,16 +290,16 @@ module core_v_mini_mcu output logic i2c_scl_o, input logic i2c_scl_i, output logic i2c_scl_oe_o, - output logic gpio_31_o, - input logic gpio_31_i, - output logic gpio_31_oe_o, + output logic gpio_30_o, + input logic gpio_30_i, + output logic gpio_30_oe_o, output logic i2c_sda_o, input logic i2c_sda_i, output logic i2c_sda_oe_o, - output logic gpio_30_o, - input logic gpio_30_i, - output logic gpio_30_oe_o, + output logic gpio_31_o, + input logic gpio_31_i, + output logic gpio_31_oe_o, // eXtension interface @@ -376,6 +407,11 @@ module core_v_mini_mcu obi_req_t peripheral_slave_req; obi_resp_t peripheral_slave_resp; + obi_req_t hyperram_req; + obi_resp_t hyperram_resp; + reg_req_t hyperbus_req; + reg_rsp_t hyperbus_rsp; + // signals to debug unit logic debug_core_req; logic debug_reset_n; @@ -584,6 +620,8 @@ module core_v_mini_mcu .ao_peripheral_slave_resp_i(ao_peripheral_slave_resp), .peripheral_slave_req_o(peripheral_slave_req), .peripheral_slave_resp_i(peripheral_slave_resp), + .hyperram_req_o(hyperram_req), + .hyperram_resp_i(hyperram_resp), .flash_mem_slave_req_o(flash_mem_slave_req), .flash_mem_slave_resp_i(flash_mem_slave_resp), .ext_core_instr_req_o(ext_core_instr_req_o), @@ -657,6 +695,8 @@ module core_v_mini_mcu .spi_flash_intr_event_o(spi_flash_intr), .pad_req_o, .pad_resp_i, + .hyperbus_req_o(hyperbus_req), + .hyperbus_rsp_i(hyperbus_rsp), .fast_intr_i(fast_intr), .fast_intr_o(irq_fast), .cio_gpio_i(gpio_ao_in), @@ -757,6 +797,44 @@ module core_v_mini_mcu end end + logic [7:0] hyper_dq_in, hyper_dq_out; + logic hyper_dq_oe; + + assign hyper_dq_in = { + hyper_dq_0_i, + hyper_dq_1_i, + hyper_dq_2_i, + hyper_dq_3_i, + hyper_dq_4_i, + hyper_dq_5_i, + hyper_dq_6_i, + hyper_dq_7_i + }; + assign {hyper_dq_0_o, hyper_dq_1_o, hyper_dq_2_o, hyper_dq_3_o, hyper_dq_4_o, hyper_dq_5_o, hyper_dq_6_o, hyper_dq_7_o} = hyper_dq_out; + assign {hyper_dq_0_oe_o, hyper_dq_1_oe_o, hyper_dq_2_oe_o, hyper_dq_3_oe_o, hyper_dq_4_oe_o, hyper_dq_5_oe_o, hyper_dq_6_oe_o, hyper_dq_7_oe_o} = {8{hyper_dq_oe}}; + + hyperbus_subsystem hyperbus_subsystem_i ( + .clk_i, + .clk_per_i(clk_i), + .rst_ni, + .obi_req_i(hyperram_req), + .obi_resp_o(hyperram_resp), + .reg_req_i(hyperbus_req), + .reg_rsp_o(hyperbus_rsp), + // Physical interace: facing HyperBus PADs + .hyper_cs_no, + .hyper_ck_o, + .hyper_ck_no(hyper_ckn_o), + .hyper_rwds_o, + .hyper_rwds_i, + .hyper_rwds_oe_o, + .hyper_dq_i(hyper_dq_in), + .hyper_dq_o(hyper_dq_out), + .hyper_dq_oe_o(hyper_dq_oe), + .hyper_reset_no + ); + + assign ext_cpu_subsystem_rst_no = cpu_subsystem_rst_n; assign ext_debug_reset_no = debug_reset_n; diff --git a/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl b/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl index cb5717528..67fcffb15 100644 --- a/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl +++ b/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl @@ -130,6 +130,11 @@ ${pad.core_v_mini_mcu_interface} obi_req_t peripheral_slave_req; obi_resp_t peripheral_slave_resp; + obi_req_t hyperram_req; + obi_resp_t hyperram_resp; + reg_req_t hyperbus_req; + reg_rsp_t hyperbus_rsp; + // signals to debug unit logic debug_core_req; logic debug_reset_n; @@ -334,6 +339,8 @@ ${pad.core_v_mini_mcu_interface} .ao_peripheral_slave_resp_i(ao_peripheral_slave_resp), .peripheral_slave_req_o(peripheral_slave_req), .peripheral_slave_resp_i(peripheral_slave_resp), + .hyperram_req_o(hyperram_req), + .hyperram_resp_i(hyperram_resp), .flash_mem_slave_req_o(flash_mem_slave_req), .flash_mem_slave_resp_i(flash_mem_slave_resp), .ext_core_instr_req_o(ext_core_instr_req_o), @@ -405,6 +412,8 @@ ${pad.core_v_mini_mcu_interface} .spi_flash_intr_event_o(spi_flash_intr), .pad_req_o, .pad_resp_i, + .hyperbus_req_o(hyperbus_req), + .hyperbus_rsp_i(hyperbus_rsp), .fast_intr_i(fast_intr), .fast_intr_o(irq_fast), .cio_gpio_i(gpio_ao_in), @@ -505,6 +514,55 @@ ${pad.core_v_mini_mcu_interface} end end + logic [7:0] hyper_dq_in, hyper_dq_out; + logic hyper_dq_oe; + + assign hyper_dq_in = { + hyper_dq_0_i, + hyper_dq_1_i, + hyper_dq_2_i, + hyper_dq_3_i, + hyper_dq_4_i, + hyper_dq_5_i, + hyper_dq_6_i, + hyper_dq_7_i + }; + assign {hyper_dq_0_o, hyper_dq_1_o, hyper_dq_2_o, hyper_dq_3_o, hyper_dq_4_o, hyper_dq_5_o, hyper_dq_6_o, hyper_dq_7_o} = hyper_dq_out; + assign {hyper_dq_0_oe_o, hyper_dq_1_oe_o, hyper_dq_2_oe_o, hyper_dq_3_oe_o, hyper_dq_4_oe_o, hyper_dq_5_oe_o, hyper_dq_6_oe_o, hyper_dq_7_oe_o} = {8{hyper_dq_oe}}; + +% if hyperram_is_included in ("yes"): + hyperbus_subsystem hyperbus_subsystem_i ( + .clk_i, + .clk_per_i(clk_i), + .rst_ni, + .obi_req_i(hyperram_req), + .obi_resp_o(hyperram_resp), + .reg_req_i(hyperbus_req), + .reg_rsp_o(hyperbus_rsp), + // Physical interace: facing HyperBus PADs + .hyper_cs_no, + .hyper_ck_o, + .hyper_ck_no(hyper_ckn_o), + .hyper_rwds_o, + .hyper_rwds_i, + .hyper_rwds_oe_o, + .hyper_dq_i(hyper_dq_in), + .hyper_dq_o(hyper_dq_out), + .hyper_dq_oe_o(hyper_dq_oe), + .hyper_reset_no + ); + +% else: + assign hyper_cs_no = '0; + assign hyper_ck_o = '0; + assign hyper_ckn_o = '0; + assign hyper_rwds_io_o = '0; + assign hyper_rwds_io_oe_o = '0; + assign hyper_dq_out = '0; + assign hyper_dq_oe = '0; + assign hyper_reset_no = '0; +% endif + assign ext_cpu_subsystem_rst_no = cpu_subsystem_rst_n; assign ext_debug_reset_no = debug_reset_n; diff --git a/hw/core-v-mini-mcu/hyperbus_subsystem.sv b/hw/core-v-mini-mcu/hyperbus_subsystem.sv new file mode 100644 index 000000000..72352e231 --- /dev/null +++ b/hw/core-v-mini-mcu/hyperbus_subsystem.sv @@ -0,0 +1,139 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Davide Schiavone + +module hyperbus_subsystem + import obi_pkg::*; + import reg_pkg::*; + import core_v_mini_mcu_pkg::*; +#( + parameter int unsigned NumChips = core_v_mini_mcu_pkg::HyperRamNumChips, + parameter int unsigned NumPhys = core_v_mini_mcu_pkg::HyperRamNumPhys +) ( + input logic clk_i, + input logic clk_per_i, + input logic rst_ni, + // OBI bus + input obi_req_t obi_req_i, + output obi_resp_t obi_resp_o, + // Reg bus + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // Physical interace: facing HyperBus PADs + output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0] hyper_ck_o, + output logic [NumPhys-1:0] hyper_ck_no, + output logic [NumPhys-1:0] hyper_rwds_o, + input logic [NumPhys-1:0] hyper_rwds_i, + output logic [NumPhys-1:0] hyper_rwds_oe_o, + input logic [NumPhys-1:0][ 7:0] hyper_dq_i, + output logic [NumPhys-1:0][ 7:0] hyper_dq_o, + output logic [NumPhys-1:0] hyper_dq_oe_o, + output logic [NumPhys-1:0] hyper_reset_no +); + + import axi_pkg::*; + + core_v_mini_mcu_pkg::axi_req_t axi_req; + core_v_mini_mcu_pkg::axi_resp_t axi_resp; + localparam int unsigned AddrMapHyperBusIdxWidth = (NumChips > 1) ? $clog2(NumChips) : 1; + + typedef struct packed { + logic [AddrMapHyperBusIdxWidth-1:0] idx; + logic [1:0] start_addr; + logic [1:0] end_addr; + } addr_map_hyperbus_t; + + // Instantiate the HyperBus controller + hyperbus #( + .NumChips(NumChips), + .NumPhys(NumPhys), + .AxiAddrWidth(core_v_mini_mcu_pkg::AxiAddrWidth), + .AxiDataWidth(core_v_mini_mcu_pkg::AxiDataWidth), + .AxiIdWidth(core_v_mini_mcu_pkg::AxiIdWidth), + .AxiUserWidth(core_v_mini_mcu_pkg::AxiUserWidth), + .axi_req_t(core_v_mini_mcu_pkg::axi_req_t), + .axi_rsp_t(core_v_mini_mcu_pkg::axi_resp_t), + .axi_w_chan_t(core_v_mini_mcu_pkg::axi_w_t), + .axi_b_chan_t(core_v_mini_mcu_pkg::axi_b_t), + .axi_ar_chan_t(core_v_mini_mcu_pkg::axi_ar_t), + .axi_r_chan_t(core_v_mini_mcu_pkg::axi_r_t), + .axi_aw_chan_t(core_v_mini_mcu_pkg::axi_aw_t), + .RegDataWidth(32), + .reg_req_t(reg_pkg::reg_req_t), + .reg_rsp_t(reg_pkg::reg_rsp_t), + .axi_rule_t(addr_map_hyperbus_t) + ) hyperbus_i ( + .clk_phy_i (clk_per_i), + .rst_phy_ni (rst_ni), + .clk_sys_i (clk_i), + .rst_sys_ni (rst_ni), + .test_mode_i(1'b0), + .axi_req_i (axi_req), + .axi_rsp_o (axi_resp), + .reg_req_i, + .reg_rsp_o, + .hyper_cs_no, + .hyper_ck_o, + .hyper_ck_no, + .hyper_rwds_o, + .hyper_rwds_i, + .hyper_rwds_oe_o, + .hyper_dq_i, + .hyper_dq_o, + .hyper_dq_oe_o, + .hyper_reset_no + ); + + axi_from_mem #( + .MemAddrWidth(32), + .AxiAddrWidth(32), + .DataWidth(32), + .MaxRequests(1), + .axi_req_t(core_v_mini_mcu_pkg::axi_req_t), + .axi_rsp_t(core_v_mini_mcu_pkg::axi_resp_t) + ) axi_from_mem_i ( + .clk_i, + .rst_ni, + .mem_req_i(obi_req_i.req), + .mem_addr_i(obi_req_i.addr), + .mem_we_i(obi_req_i.we), + .mem_wdata_i(obi_req_i.wdata), + .mem_be_i(obi_req_i.be), + .mem_gnt_o(obi_resp_o.gnt), + .mem_rsp_valid_o(obi_resp_o.rvalid), + .mem_rsp_rdata_o(obi_resp_o.rdata), + .mem_rsp_error_o(), + .slv_aw_cache_i('0), + .slv_ar_cache_i('0), + .axi_req_o(axi_req), + .axi_rsp_i(axi_resp) + ); + +`ifndef SYNTHESIS + + /* + this version of axi from mem uses axi_lite_from_mem + as axi lite does not support any transaction size but the buswidth (32b for X-HEEP) + you cannot access the HyperRam with data bidwidth < 32b (e.g. 8 and 16) + + Future fixes will come by bypassing the axi_lite bridge and using the OBI BE (byte enable) to infer the size. + For now, this limitation must be taken into account in SW - so if 8b or 16b data needs to be used, we recocomend threat them packet + + further constraints have been added by checking that the OBI BE must be always 4'b1111 in Simulation (but not FPGA or ASIC - thus be careful) + + */ + + always_ff @(posedge clk_i, negedge rst_ni) begin : check_out_of_bound + if (rst_ni) begin + if (obi_req_i.req && ((obi_req_i.addr[1:0] != 2'b00) || obi_req_i.be != 4'b1111)) begin + $display("%t wrong HyperRam access 0x%08x (be: %x)", $time, obi_req_i.addr, obi_req_i.be); + $stop; + end + end + end +`endif + +endmodule : hyperbus_subsystem diff --git a/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl b/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl index ba99d50f5..c7fca9a65 100644 --- a/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl +++ b/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl @@ -13,6 +13,8 @@ * */ +`include "axi/typedef.svh" + package core_v_mini_mcu_pkg; import addr_map_rule_pkg::*; @@ -49,7 +51,7 @@ package core_v_mini_mcu_pkg; //must be power of two localparam int unsigned MEM_SIZE = 32'h${f'{xheep.ram_size_address():08X}'}; - localparam SYSTEM_XBAR_NSLAVE = ${xheep.ram_numbanks() + 5}; + localparam SYSTEM_XBAR_NSLAVE = ${xheep.ram_numbanks() + 6}; localparam int unsigned LOG_SYSTEM_XBAR_NMASTER = SYSTEM_XBAR_NMASTER > 1 ? $clog2(SYSTEM_XBAR_NMASTER) : 32'd1; localparam int unsigned LOG_SYSTEM_XBAR_NSLAVE = SYSTEM_XBAR_NSLAVE > 1 ? $clog2(SYSTEM_XBAR_NSLAVE) : 32'd1; @@ -97,6 +99,11 @@ package core_v_mini_mcu_pkg; localparam logic[31:0] FLASH_MEM_END_ADDRESS = FLASH_MEM_START_ADDRESS + FLASH_MEM_SIZE; localparam logic[31:0] FLASH_MEM_IDX = 32'd${xheep.ram_numbanks() + 4}; + localparam logic[31:0] HYPERRAM_MEM_START_ADDRESS = 32'h${hyperram_mem_start_address}; + localparam logic[31:0] HYPERRAM_MEM_SIZE = 32'h${hyperram_mem_size_address}; + localparam logic[31:0] HYPERRAM_MEM_END_ADDRESS = HYPERRAM_MEM_START_ADDRESS + HYPERRAM_MEM_SIZE; + localparam logic[31:0] HYPERRAM_MEM_IDX = 32'd${xheep.ram_numbanks() + 5}; + localparam addr_map_rule_t [SYSTEM_XBAR_NSLAVE-1:0] XBAR_ADDR_RULES = '{ '{ idx: ERROR_IDX, start_addr: ERROR_START_ADDRESS, end_addr: ERROR_END_ADDRESS }, % for bank in xheep.iter_ram_banks(): @@ -105,7 +112,8 @@ package core_v_mini_mcu_pkg; '{ idx: DEBUG_IDX, start_addr: DEBUG_START_ADDRESS, end_addr: DEBUG_END_ADDRESS }, '{ idx: AO_PERIPHERAL_IDX, start_addr: AO_PERIPHERAL_START_ADDRESS, end_addr: AO_PERIPHERAL_END_ADDRESS }, '{ idx: PERIPHERAL_IDX, start_addr: PERIPHERAL_START_ADDRESS, end_addr: PERIPHERAL_END_ADDRESS }, - '{ idx: FLASH_MEM_IDX, start_addr: FLASH_MEM_START_ADDRESS, end_addr: FLASH_MEM_END_ADDRESS } + '{ idx: FLASH_MEM_IDX, start_addr: FLASH_MEM_START_ADDRESS, end_addr: FLASH_MEM_END_ADDRESS }, + '{ idx: HYPERRAM_MEM_IDX, start_addr: HYPERRAM_MEM_START_ADDRESS, end_addr: HYPERRAM_MEM_END_ADDRESS } }; // External slave address map @@ -217,4 +225,38 @@ package core_v_mini_mcu_pkg; LEFT } pad_side_e; + // HyperRam + localparam int unsigned HyperRamNumChips = 1; + localparam int unsigned HyperRamNumPhys = 1; + + // AXI definitions + parameter int unsigned AxiAddrWidth = 32; + parameter int unsigned AxiDataWidth = 32; + parameter int unsigned AxiIdWidth = 8; + parameter int unsigned AxiUserWidth = 8; + parameter int unsigned RegDataWidth = 32; + parameter int unsigned StreamDataBytes = 32; + localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; + localparam int unsigned RegStrbWidth = RegDataWidth / 8; + typedef logic [StreamDataBytes*8-1:0] tdata_t; + typedef logic [StreamDataBytes-1:0] tstrb_t; + typedef logic [StreamDataBytes-1:0] tkeep_t; + typedef logic tlast_t; + typedef logic id_t; + typedef logic tdest_t; + typedef logic tuser_t; + typedef logic tready_t; + typedef logic[AxiAddrWidth-1:0] axi_addr_t; + typedef logic[AxiDataWidth-1:0] axi_data_t; + typedef logic[AxiStrbWidth-1:0] axi_strb_t; + typedef logic[AxiUserWidth-1:0] axi_user_t; + typedef logic[AxiIdWidth-1:0] axi_id_t; + `AXI_TYPEDEF_AW_CHAN_T(axi_aw_t, axi_addr_t, axi_id_t, axi_user_t) + `AXI_TYPEDEF_W_CHAN_T(axi_w_t, axi_data_t, axi_strb_t, axi_user_t) + `AXI_TYPEDEF_B_CHAN_T(axi_b_t, axi_id_t, axi_user_t) + `AXI_TYPEDEF_AR_CHAN_T(axi_ar_t, axi_addr_t, axi_id_t, axi_user_t) + `AXI_TYPEDEF_R_CHAN_T(axi_r_t, axi_data_t, axi_id_t, axi_user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, axi_aw_t, axi_w_t, axi_ar_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, axi_b_t, axi_r_t) + endpackage diff --git a/hw/core-v-mini-mcu/include/obi_pkg.sv b/hw/core-v-mini-mcu/include/obi_pkg.sv index e3f73fc45..6ceec5f09 100644 --- a/hw/core-v-mini-mcu/include/obi_pkg.sv +++ b/hw/core-v-mini-mcu/include/obi_pkg.sv @@ -10,16 +10,16 @@ package obi_pkg; typedef struct packed { - logic req; - logic we; - logic [3:0] be; + logic req; + logic we; + logic [3:0] be; logic [31:0] addr; logic [31:0] wdata; } obi_req_t; typedef struct packed { - logic gnt; - logic rvalid; + logic gnt; + logic rvalid; logic [31:0] rdata; } obi_resp_t; diff --git a/hw/core-v-mini-mcu/include/reg_pkg.sv b/hw/core-v-mini-mcu/include/reg_pkg.sv index 6af8dc8d8..d6ff62ff4 100644 --- a/hw/core-v-mini-mcu/include/reg_pkg.sv +++ b/hw/core-v-mini-mcu/include/reg_pkg.sv @@ -16,16 +16,16 @@ package reg_pkg; typedef struct packed { - logic valid; - logic write; - logic [3:0] wstrb; + logic valid; + logic write; + logic [3:0] wstrb; logic [31:0] addr; logic [31:0] wdata; } reg_req_t; typedef struct packed { - logic error; - logic ready; + logic error; + logic ready; logic [31:0] rdata; } reg_rsp_t; diff --git a/hw/core-v-mini-mcu/include/x-heep_packages.core b/hw/core-v-mini-mcu/include/x-heep_packages.core index 6cbf61b01..9e450bbb7 100644 --- a/hw/core-v-mini-mcu/include/x-heep_packages.core +++ b/hw/core-v-mini-mcu/include/x-heep_packages.core @@ -9,6 +9,8 @@ description: "toplevel packages of x-heep" filesets: files_rtl: + depend: + - pulp-platform.org::axi_heep files: - addr_map_rule_pkg.sv - obi_pkg.sv diff --git a/hw/core-v-mini-mcu/system_bus.sv.tpl b/hw/core-v-mini-mcu/system_bus.sv.tpl index 19bb8622a..633a4ae25 100644 --- a/hw/core-v-mini-mcu/system_bus.sv.tpl +++ b/hw/core-v-mini-mcu/system_bus.sv.tpl @@ -66,6 +66,9 @@ module system_bus output obi_req_t flash_mem_slave_req_o, input obi_resp_t flash_mem_slave_resp_i, + output obi_req_t hyperram_req_o, + input obi_resp_t hyperram_resp_i, + // External slave ports output obi_req_t ext_core_instr_req_o, input obi_resp_t ext_core_instr_resp_i, @@ -164,6 +167,7 @@ module system_bus assign ao_peripheral_slave_req_o = int_slave_req[core_v_mini_mcu_pkg::AO_PERIPHERAL_IDX]; assign peripheral_slave_req_o = int_slave_req[core_v_mini_mcu_pkg::PERIPHERAL_IDX]; assign flash_mem_slave_req_o = int_slave_req[core_v_mini_mcu_pkg::FLASH_MEM_IDX]; + assign hyperram_req_o = int_slave_req[core_v_mini_mcu_pkg::HYPERRAM_MEM_IDX]; // External slave requests assign ext_core_instr_req_o = demux_xbar_req[CORE_INSTR_IDX][DEMUX_XBAR_EXT_SLAVE_IDX]; @@ -182,6 +186,7 @@ module system_bus assign int_slave_resp[core_v_mini_mcu_pkg::AO_PERIPHERAL_IDX] = ao_peripheral_slave_resp_i; assign int_slave_resp[core_v_mini_mcu_pkg::PERIPHERAL_IDX] = peripheral_slave_resp_i; assign int_slave_resp[core_v_mini_mcu_pkg::FLASH_MEM_IDX] = flash_mem_slave_resp_i; + assign int_slave_resp[core_v_mini_mcu_pkg::HYPERRAM_MEM_IDX] = hyperram_resp_i; // External slave responses assign demux_xbar_resp[CORE_INSTR_IDX][DEMUX_XBAR_EXT_SLAVE_IDX] = ext_core_instr_resp_i; diff --git a/hw/fpga/xilinx_core_v_mini_mcu_wrapper.sv b/hw/fpga/xilinx_core_v_mini_mcu_wrapper.sv index fc9c53fb0..2d6d6f870 100644 --- a/hw/fpga/xilinx_core_v_mini_mcu_wrapper.sv +++ b/hw/fpga/xilinx_core_v_mini_mcu_wrapper.sv @@ -36,7 +36,7 @@ module xilinx_core_v_mini_mcu_wrapper inout logic uart_rx_i, inout logic uart_tx_o, - inout logic [17:0] gpio_io, + inout logic [13:0] gpio_io, output logic exit_value_o, inout logic exit_valid_o, @@ -49,22 +49,30 @@ module xilinx_core_v_mini_mcu_wrapper inout logic spi_csb_o, inout logic spi_sck_o, - inout logic [3:0] spi2_sd_io, - inout logic [1:0] spi2_csb_o, - inout logic spi2_sck_o, - inout logic i2c_scl_io, inout logic i2c_sda_io, - inout logic pdm2pcm_clk_io, - inout logic pdm2pcm_pdm_io, - inout logic i2s_sck_io, inout logic i2s_ws_io, - inout logic i2s_sd_io + inout logic i2s_sd_io, + + inout logic hyper_cs_nio, + inout logic hyper_ck_io, + inout logic hyper_ckn_io, + inout logic hyper_rwds_io_io, + inout logic hyper_reset_nio, + inout logic hyper_dq_0_io_io, + inout logic hyper_dq_1_io_io, + inout logic hyper_dq_2_io_io, + inout logic hyper_dq_3_io_io, + inout logic hyper_dq_4_io_io, + inout logic hyper_dq_5_io_io, + inout logic hyper_dq_6_io_io, + inout logic hyper_dq_7_io_io ); + wire clk_gen; logic [ 31:0] exit_value; wire rst_n; @@ -141,6 +149,8 @@ module xilinx_core_v_mini_mcu_wrapper .ext_dma_addr_ch0_resp_i('0), .ext_peripheral_slave_req_o(), .ext_peripheral_slave_resp_i('0), + .ext_dma_slot_tx_i('0), + .ext_dma_slot_rx_i('0), .external_subsystem_powergate_switch_no(), .external_subsystem_powergate_switch_ack_ni(), .external_subsystem_powergate_iso_no(), @@ -174,10 +184,6 @@ module xilinx_core_v_mini_mcu_wrapper .gpio_11_io(gpio_io[11]), .gpio_12_io(gpio_io[12]), .gpio_13_io(gpio_io[13]), - .gpio_14_io(gpio_io[14]), - .gpio_15_io(gpio_io[15]), - .gpio_16_io(gpio_io[16]), - .gpio_17_io(gpio_io[17]), .spi_flash_sd_0_io(spi_flash_sd_io[0]), .spi_flash_sd_1_io(spi_flash_sd_io[1]), .spi_flash_sd_2_io(spi_flash_sd_io[2]), @@ -194,20 +200,23 @@ module xilinx_core_v_mini_mcu_wrapper .spi_sck_io(spi_sck_o), .i2c_scl_io, .i2c_sda_io, - .spi2_sd_0_io(spi2_sd_io[0]), - .spi2_sd_1_io(spi2_sd_io[1]), - .spi2_sd_2_io(spi2_sd_io[2]), - .spi2_sd_3_io(spi2_sd_io[3]), - .spi2_cs_0_io(spi2_csb_o[0]), - .spi2_cs_1_io(spi2_csb_o[1]), - .spi2_sck_io(spi2_sck_o), - .pdm2pcm_clk_io, - .pdm2pcm_pdm_io, .i2s_sck_io(i2s_sck_io), .i2s_ws_io(i2s_ws_io), .i2s_sd_io(i2s_sd_io), - .ext_dma_slot_tx_i('0), - .ext_dma_slot_rx_i('0) + .hyper_cs_nio, + .hyper_ck_io, + .hyper_ckn_io, + .hyper_rwds_io_io, + .hyper_reset_nio, + .hyper_dq_0_io_io, + .hyper_dq_1_io_io, + .hyper_dq_2_io_io, + .hyper_dq_3_io_io, + .hyper_dq_4_io_io, + .hyper_dq_5_io_io, + .hyper_dq_6_io_io, + .hyper_dq_7_io_io + ); assign exit_value_o = exit_value[0]; diff --git a/hw/ip/boot_rom/boot_rom.core b/hw/ip/boot_rom/boot_rom.core index c17ca1cf8..3db2090c9 100644 --- a/hw/ip/boot_rom/boot_rom.core +++ b/hw/ip/boot_rom/boot_rom.core @@ -11,6 +11,7 @@ filesets: files_rtl: depend: - pulp-platform.org::register_interface + - x-heep::packages files: - boot_rom.sv file_type: systemVerilogSource diff --git a/hw/ip/dma/dma.core b/hw/ip/dma/dma.core index 86255b7a1..f49582aa2 100644 --- a/hw/ip/dma/dma.core +++ b/hw/ip/dma/dma.core @@ -11,6 +11,7 @@ filesets: files_rtl: depend: - pulp-platform.org::common_cells + - x-heep::packages files: - rtl/dma_reg_pkg.sv - rtl/dma_reg_top.sv diff --git a/hw/ip/dma/rtl/dma.sv b/hw/ip/dma/rtl/dma.sv index 4bfe0219c..91f7ac7cf 100644 --- a/hw/ip/dma/rtl/dma.sv +++ b/hw/ip/dma/rtl/dma.sv @@ -42,125 +42,125 @@ module dma #( localparam int unsigned LastFifoUsage = FIFO_DEPTH - 1; localparam int unsigned Addr_Fifo_Depth = (FIFO_DEPTH > 1) ? $clog2(FIFO_DEPTH) : 1; - dma_reg2hw_t reg2hw; - dma_hw2reg_t hw2reg; - - logic [ 31:0] src_ptr_reg; - logic [ 31:0] read_ptr_reg; - logic [ 31:0] addr_ptr_reg; - logic [ 31:0] read_ptr_valid_reg; - logic [ 31:0] write_ptr_reg; - logic [ 31:0] write_address; - logic [ 31:0] dma_addr_cnt; - logic [ 2:0] dma_src_cnt_du; - logic [ 2:0] dma_dst_cnt_du; - logic dma_start; - logic dma_done; - logic dma_window_event; - - logic window_done_q; - - logic [Addr_Fifo_Depth-1:0] fifo_usage; - logic fifo_alm_full; - - logic [Addr_Fifo_Depth-1:0] fifo_addr_usage; - logic fifo_addr_alm_full; - - logic data_in_req; - logic data_in_we; - logic [ 3:0] data_in_be; - logic [ 31:0] data_in_addr; - logic data_in_gnt; - logic data_in_rvalid; - logic [ 31:0] data_in_rdata; - - logic data_addr_in_req; - logic data_addr_in_we; - logic [ 3:0] data_addr_in_be; - logic [ 31:0] data_addr_in_addr; - logic data_addr_in_gnt; - logic data_addr_in_rvalid; - logic [ 31:0] data_addr_in_rdata; - - logic data_out_req; - logic data_out_we; - logic [ 3:0] data_out_be; - logic [ 31:0] data_out_addr; - logic [ 31:0] data_out_wdata; - logic data_out_gnt; - logic data_out_rvalid; - logic [ 31:0] data_out_rdata; + dma_reg2hw_t reg2hw; + dma_hw2reg_t hw2reg; + + logic [31:0] src_ptr_reg; + logic [31:0] read_ptr_reg; + logic [31:0] addr_ptr_reg; + logic [31:0] read_ptr_valid_reg; + logic [31:0] write_ptr_reg; + logic [31:0] write_address; + logic [31:0] dma_addr_cnt; + logic [2:0] dma_src_cnt_du; + logic [2:0] dma_dst_cnt_du; + logic dma_start; + logic dma_done; + logic dma_window_event; + + logic window_done_q; + + logic [Addr_Fifo_Depth-1:0] fifo_usage; + logic fifo_alm_full; + + logic [Addr_Fifo_Depth-1:0] fifo_addr_usage; + logic fifo_addr_alm_full; + + logic data_in_req; + logic data_in_we; + logic [3:0] data_in_be; + logic [31:0] data_in_addr; + logic data_in_gnt; + logic data_in_rvalid; + logic [31:0] data_in_rdata; + + logic data_addr_in_req; + logic data_addr_in_we; + logic [3:0] data_addr_in_be; + logic [31:0] data_addr_in_addr; + logic data_addr_in_gnt; + logic data_addr_in_rvalid; + logic [31:0] data_addr_in_rdata; + + logic data_out_req; + logic data_out_we; + logic [3:0] data_out_be; + logic [31:0] data_out_addr; + logic [31:0] data_out_wdata; + logic data_out_gnt; + logic data_out_rvalid; + logic [31:0] data_out_rdata; /* Sign extension signals */ - logic sign_extend; + logic sign_extend; /* 2D signals */ /* Dimensionality configuration */ - logic dma_conf_1d; // Dimensionality configuration: 0-> 1D, 1-> 2D - logic dma_conf_2d; // Dimensionality configuration: 0-> 1D, 1-> 2D + logic dma_conf_1d; // Dimensionality configuration: 0-> 1D, 1-> 2D + logic dma_conf_2d; // Dimensionality configuration: 0-> 1D, 1-> 2D /* Counters */ - logic [ 16:0] dma_src_cnt_d1; // d1 src counter - logic [ 16:0] dma_src_cnt_d2; // d2 src counter - logic [ 16:0] dma_dst_cnt_d1; // d2 dst counter + logic [16:0] dma_src_cnt_d1; // d1 src counter + logic [16:0] dma_src_cnt_d2; // d2 src counter + logic [16:0] dma_dst_cnt_d1; // d2 dst counter /* Increments */ - logic [ 5:0] dma_src_d1_inc; // d1 source increment - logic [ 22:0] dma_src_d2_inc; // d2 source increment - logic [ 5:0] dma_dst_d1_inc; // d1 destination increment - logic [ 22:0] dma_dst_d2_inc; // d2 destination increment + logic [5:0] dma_src_d1_inc; // d1 source increment + logic [22:0] dma_src_d2_inc; // d2 source increment + logic [5:0] dma_dst_d1_inc; // d1 destination increment + logic [22:0] dma_dst_d2_inc; // d2 destination increment /* Flags */ - logic pad_fifo_on; // Padding flag for FIFO - logic pad_cnt_on; // Padding flag for counters - logic read_ptr_update_sel; // Select the read pointer update source + logic pad_fifo_on; // Padding flag for FIFO + logic pad_cnt_on; // Padding flag for counters + logic read_ptr_update_sel; // Select the read pointer update source /* Padding FSM conditions */ - logic idle_to_left_ex; - logic idle_to_top_ex; - logic idle_to_right_ex; - logic idle_to_bottom_ex; - logic top_ex_to_top_dn; - logic top_ex_to_left_ex; - logic top_dn_to_right_ex; - logic top_dn_to_bottom_ex; - logic top_dn_to_idle; - logic left_ex_to_left_dn; - logic left_dn_to_left_ex; - logic left_dn_to_right_ex; - logic left_dn_to_bottom_ex; - logic left_dn_to_idle; - logic right_ex_to_right_dn; - logic right_ex_to_left_ex; - logic right_dn_to_right_ex; - logic right_dn_to_idle; - logic right_ex_to_bottom_ex; - logic bottom_ex_to_idle; + logic idle_to_left_ex; + logic idle_to_top_ex; + logic idle_to_right_ex; + logic idle_to_bottom_ex; + logic top_ex_to_top_dn; + logic top_ex_to_left_ex; + logic top_dn_to_right_ex; + logic top_dn_to_bottom_ex; + logic top_dn_to_idle; + logic left_ex_to_left_dn; + logic left_dn_to_left_ex; + logic left_dn_to_right_ex; + logic left_dn_to_bottom_ex; + logic left_dn_to_idle; + logic right_ex_to_right_dn; + logic right_ex_to_left_ex; + logic right_dn_to_right_ex; + logic right_dn_to_idle; + logic right_ex_to_bottom_ex; + logic bottom_ex_to_idle; /* Padding synchronization signals */ - logic data_in_rvalid_virt; - logic data_in_rvalid_virt_n; - logic data_in_rvalid_virt_n_n; - logic data_in_gnt_virt; - logic data_in_gnt_virt_n; - logic data_in_gnt_virt_n_n; + logic data_in_rvalid_virt; + logic data_in_rvalid_virt_n; + logic data_in_rvalid_virt_n_n; + logic data_in_gnt_virt; + logic data_in_gnt_virt_n; + logic data_in_gnt_virt_n_n; /* Interrupt Flag Register signals */ - logic transaction_ifr; - logic dma_done_intr_n; - logic dma_done_intr; - logic window_ifr; - logic dma_window_intr; - logic dma_window_intr_n; + logic transaction_ifr; + logic dma_done_intr_n; + logic dma_done_intr; + logic window_ifr; + logic dma_window_intr; + logic dma_window_intr_n; /* FIFO signals */ - logic fifo_flush; - logic fifo_full; - logic fifo_empty; + logic fifo_flush; + logic fifo_full; + logic fifo_empty; - logic fifo_addr_flush; - logic fifo_addr_full; + logic fifo_addr_flush; + logic fifo_addr_full; logic fifo_addr_empty, fifo_addr_empty_check; logic wait_for_rx; @@ -298,53 +298,133 @@ module dma #( }; assign idle_to_right_ex = { |reg2hw.pad_top.q == 1'b0 && |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b1 - && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) + && dma_src_cnt_d1 == ({ + 11'h0, reg2hw.pad_right.q + } + { + 14'h0, dma_dst_cnt_du + }) }; assign idle_to_bottom_ex = { |reg2hw.pad_top.q == 1'b0 && |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 - && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) + && dma_src_cnt_d2 == ({ + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) }; assign top_ex_to_top_dn = { - dma_src_cnt_d2 == ({1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b0 + dma_src_cnt_d2 == ({ + 1'h0, reg2hw.size_d2.q + } + { + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) && |reg2hw.pad_left.q == 1'b0 }; assign top_ex_to_left_ex = { - dma_src_cnt_d2 == ({1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b1 + dma_src_cnt_d2 == ({ + 1'h0, reg2hw.size_d2.q + } + { + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) && |reg2hw.pad_left.q == 1'b1 }; assign top_dn_to_right_ex = { - |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) + |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({ + 11'h0, reg2hw.pad_right.q + } + { + 14'h0, dma_dst_cnt_du + }) }; assign top_dn_to_bottom_ex = { - |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) + |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({ + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) }; assign top_dn_to_idle = { |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b0 && |dma_src_cnt_d2 == 1'b0 }; assign left_ex_to_left_dn = { - dma_src_cnt_d1 == ({1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) + dma_src_cnt_d1 == ({ + 1'h0, reg2hw.size_d1.q + } + { + 11'h0, reg2hw.pad_right.q + } + { + 14'h0, dma_dst_cnt_du + }) }; assign left_dn_to_left_ex = { - dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && dma_src_cnt_d2 != ({14'h0, dma_dst_cnt_du} + {11'h0, reg2hw.pad_bottom.q}) && |reg2hw.pad_right.q == 1'b0 + dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d2 != ({ + 14'h0, dma_dst_cnt_du + } + { + 11'h0, reg2hw.pad_bottom.q + }) && |reg2hw.pad_right.q == 1'b0 }; assign left_dn_to_right_ex = { - |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) + |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({ + 11'h0, reg2hw.pad_right.q + } + { + 14'h0, dma_dst_cnt_du + }) }; assign left_dn_to_bottom_ex = { - |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) + |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({ + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) }; assign left_dn_to_idle = { |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b0 && |dma_src_cnt_d2 == 1'b0 }; assign right_ex_to_right_dn = { - dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && dma_src_cnt_d2 != ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b0 + dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d2 != ({ + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && |reg2hw.pad_left.q == 1'b0 }; assign right_ex_to_left_ex = { - dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && dma_src_cnt_d2 != ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b1 + dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d2 != ({ + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && |reg2hw.pad_left.q == 1'b1 }; assign right_ex_to_bottom_ex = { - |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) + |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({ + 11'h0, reg2hw.pad_bottom.q + } + { + 14'h0, dma_dst_cnt_du + }) && dma_src_cnt_d1 == ({ + 14'h0, dma_dst_cnt_du + }) }; assign right_dn_to_right_ex = { - dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b0 + dma_src_cnt_d1 == ({ + 11'h0, reg2hw.pad_right.q + } + { + 14'h0, dma_dst_cnt_du + }) && |reg2hw.pad_left.q == 1'b0 }; assign right_dn_to_idle = {|reg2hw.pad_bottom.q == 1'b0 && |dma_src_cnt_d2 == 1'b0}; assign bottom_ex_to_idle = { @@ -431,14 +511,18 @@ module dma #( /* In this case, the d1 is almost finished, so we need to increment the pointer by sizeof(d1)*data_unit */ read_ptr_reg <= read_ptr_reg + {9'h0, dma_src_d2_inc}; end else begin - read_ptr_reg <= read_ptr_reg + {26'h0, dma_src_d1_inc}; /* Increment of the d1 increment (stride) */ + read_ptr_reg <= read_ptr_reg + { + 26'h0, dma_src_d1_inc + }; /* Increment of the d1 increment (stride) */ end end else begin if (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du} && |dma_src_cnt_d2 == 1'b1) begin /* In this case, the d1 is almost finished, so we need to increment the pointer by sizeof(d2)*data_unit */ read_ptr_reg <= src_ptr_reg; end else begin - read_ptr_reg <= read_ptr_reg + {9'h0, dma_src_d2_inc}; /* Increment of the d1 increment (stride) */ + read_ptr_reg <= read_ptr_reg + { + 9'h0, dma_src_d2_inc + }; /* Increment of the d1 increment (stride) */ end end end @@ -458,7 +542,9 @@ module dma #( if (dma_start == 1'b1) begin src_ptr_reg <= reg2hw.src_ptr.q + {26'h0, dma_src_d1_inc}; end else if (data_in_gnt == 1'b1 && dma_conf_2d == 1'b1 && pad_cnt_on == 1'b0 && read_ptr_update_sel == 1'b1 && - (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du} && |dma_src_cnt_d2 == 1'b1)) begin + (dma_src_cnt_d1 == { + 14'h0, dma_src_cnt_du + } && |dma_src_cnt_d2 == 1'b1)) begin src_ptr_reg <= src_ptr_reg + {26'h0, dma_src_d1_inc}; end end @@ -507,7 +593,9 @@ module dma #( // In this case, the d1 is finished, so we need to increment the pointer by sizeof(d1)*data_unit*strides write_ptr_reg <= write_ptr_reg + {9'h0, dma_dst_d2_inc}; end else begin - write_ptr_reg <= write_ptr_reg + {26'h0, dma_dst_d1_inc}; // Increment just of one du, since we need to increase the 1d + write_ptr_reg <= write_ptr_reg + { + 26'h0, dma_dst_d1_inc + }; // Increment just of one du, since we need to increase the 1d end end end @@ -522,8 +610,20 @@ module dma #( dma_src_cnt_d2 <= '0; end else begin if (dma_start == 1'b1) begin - dma_src_cnt_d1 <= {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q}; - dma_src_cnt_d2 <= {1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_top.q} + {11'h0, reg2hw.pad_bottom.q}; + dma_src_cnt_d1 <= { + 1'h0, reg2hw.size_d1.q + } + { + 11'h0, reg2hw.pad_left.q + } + { + 11'h0, reg2hw.pad_right.q + }; + dma_src_cnt_d2 <= { + 1'h0, reg2hw.size_d2.q + } + { + 11'h0, reg2hw.pad_top.q + } + { + 11'h0, reg2hw.pad_bottom.q + }; end else if (data_in_gnt == 1'b1) begin if (dma_conf_1d == 1'b1) begin // 1D case @@ -533,7 +633,13 @@ module dma #( if (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du}) begin // In this case, the d1 is finished, so we need to decrement the d2 size and reset the d2 size dma_src_cnt_d2 <= dma_src_cnt_d2 - {14'h0, dma_src_cnt_du}; - dma_src_cnt_d1 <= {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q}; + dma_src_cnt_d1 <= { + 1'h0, reg2hw.size_d1.q + } + { + 11'h0, reg2hw.pad_left.q + } + { + 11'h0, reg2hw.pad_right.q + }; end else begin // In this case, the d1 isn't finished, so we need to decrement the d1 size dma_src_cnt_d1 <= dma_src_cnt_d1 - {14'h0, dma_src_cnt_du}; @@ -553,7 +659,13 @@ module dma #( dma_dst_cnt_d1 <= '0; end else begin if (dma_start == 1'b1) begin - dma_dst_cnt_d1 <= {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q}; + dma_dst_cnt_d1 <= { + 1'h0, reg2hw.size_d1.q + } + { + 11'h0, reg2hw.pad_left.q + } + { + 11'h0, reg2hw.pad_right.q + }; end else if (data_out_gnt == 1'b1) begin if (dma_conf_1d == 1'b1) begin // 1D case @@ -562,7 +674,13 @@ module dma #( // 2D case if (dma_dst_cnt_d1 == {14'h0, dma_dst_cnt_du}) begin // In this case, the d1 is finished, so we need to reset the d2 size - dma_dst_cnt_d1 <= {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q}; + dma_dst_cnt_d1 <= { + 1'h0, reg2hw.size_d1.q + } + { + 11'h0, reg2hw.pad_left.q + } + { + 11'h0, reg2hw.pad_right.q + }; end else begin // In this case, the d1 isn't finished, so we need to decrement the d1 size dma_dst_cnt_d1 <= dma_dst_cnt_d1 - {14'h0, dma_dst_cnt_du}; @@ -1057,7 +1175,13 @@ module dma #( end end else if (dma_conf_2d == 1'b1) begin // 2D DMA case: exit only if both 1d and 2d counters are at 0 - if (dma_src_cnt_d1 == {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q} && |dma_src_cnt_d2 == 1'b0) begin + if (dma_src_cnt_d1 == { + 1'h0, reg2hw.size_d1.q + } + { + 11'h0, reg2hw.pad_left.q + } + { + 11'h0, reg2hw.pad_right.q + } && |dma_src_cnt_d2 == 1'b0) begin dma_read_fsm_n_state = DMA_READ_FSM_IDLE; end else begin // The read operation is the same in both cases @@ -1216,7 +1340,9 @@ module dma #( // WINDOW EVENT // Count gnt write transaction and generate event pulse if WINDOW_SIZE is reached - assign dma_window_event = |reg2hw.window_size.q & data_out_gnt & (window_counter + 'h1 >= {19'h0, reg2hw.window_size.q}); + assign dma_window_event = |reg2hw.window_size.q & data_out_gnt & (window_counter + 'h1 >= { + 19'h0, reg2hw.window_size.q + }); always_ff @(posedge clk_i, negedge rst_ni) begin : proc_dma_window_cnt if (~rst_ni) begin diff --git a/hw/ip/dma/rtl/dma_reg_pkg.sv b/hw/ip/dma/rtl/dma_reg_pkg.sv index f2fe47617..3fc482969 100644 --- a/hw/ip/dma/rtl/dma_reg_pkg.sv +++ b/hw/ip/dma/rtl/dma_reg_pkg.sv @@ -21,12 +21,12 @@ package dma_reg_pkg; typedef struct packed { logic [15:0] q; - logic qe; + logic qe; } dma_reg2hw_size_d1_reg_t; typedef struct packed { logic [15:0] q; - logic qe; + logic qe; } dma_reg2hw_size_d2_reg_t; typedef struct packed { @@ -67,22 +67,22 @@ package dma_reg_pkg; typedef struct packed { logic [5:0] q; - logic qe; + logic qe; } dma_reg2hw_pad_top_reg_t; typedef struct packed { logic [5:0] q; - logic qe; + logic qe; } dma_reg2hw_pad_bottom_reg_t; typedef struct packed { logic [5:0] q; - logic qe; + logic qe; } dma_reg2hw_pad_right_reg_t; typedef struct packed { logic [5:0] q; - logic qe; + logic qe; } dma_reg2hw_pad_left_reg_t; typedef struct packed {logic [12:0] q;} dma_reg2hw_window_size_reg_t; @@ -111,7 +111,7 @@ package dma_reg_pkg; typedef struct packed { logic [7:0] d; - logic de; + logic de; } dma_hw2reg_window_count_reg_t; typedef struct packed {logic d;} dma_hw2reg_transaction_ifr_reg_t; diff --git a/hw/ip/dma_subsystem/dma_subsystem.core b/hw/ip/dma_subsystem/dma_subsystem.core index 1fa1b17bf..c4ebde586 100644 --- a/hw/ip/dma_subsystem/dma_subsystem.core +++ b/hw/ip/dma_subsystem/dma_subsystem.core @@ -11,6 +11,8 @@ filesets: files_rtl: depend: - pulp-platform.org::common_cells + - x-heep::packages + - x-heep:ip:dma files: - rtl/dma_subsystem.sv file_type: systemVerilogSource diff --git a/hw/ip/fast_intr_ctrl/fast_intr_ctrl.core b/hw/ip/fast_intr_ctrl/fast_intr_ctrl.core index fabd39e3d..e8e034853 100644 --- a/hw/ip/fast_intr_ctrl/fast_intr_ctrl.core +++ b/hw/ip/fast_intr_ctrl/fast_intr_ctrl.core @@ -12,6 +12,7 @@ filesets: depend: - lowrisc:prim:all - pulp-platform.org::register_interface + - x-heep::packages files: - rtl/fast_intr_ctrl_reg_pkg.sv - rtl/fast_intr_ctrl_reg_top.sv diff --git a/hw/ip/fast_intr_ctrl/rtl/fast_intr_ctrl_reg_pkg.sv b/hw/ip/fast_intr_ctrl/rtl/fast_intr_ctrl_reg_pkg.sv index 545ac60bc..d57241bc5 100644 --- a/hw/ip/fast_intr_ctrl/rtl/fast_intr_ctrl_reg_pkg.sv +++ b/hw/ip/fast_intr_ctrl/rtl/fast_intr_ctrl_reg_pkg.sv @@ -21,12 +21,12 @@ package fast_intr_ctrl_reg_pkg; typedef struct packed { logic [14:0] d; - logic de; + logic de; } fast_intr_ctrl_hw2reg_fast_intr_pending_reg_t; typedef struct packed { logic [14:0] d; - logic de; + logic de; } fast_intr_ctrl_hw2reg_fast_intr_clear_reg_t; // Register -> HW type diff --git a/hw/ip/i2s/i2s.core b/hw/ip/i2s/i2s.core index c43797990..358d28df1 100644 --- a/hw/ip/i2s/i2s.core +++ b/hw/ip/i2s/i2s.core @@ -11,6 +11,7 @@ filesets: files_rtl: depend: - pulp-platform.org::common_cells + - x-heep::packages files: - rtl/i2s_reg_pkg.sv - rtl/i2s_reg_top.sv diff --git a/hw/ip/i2s/rtl/i2s_reg_pkg.sv b/hw/ip/i2s/rtl/i2s_reg_pkg.sv index 8e612aa22..5e921f956 100644 --- a/hw/ip/i2s/rtl/i2s_reg_pkg.sv +++ b/hw/ip/i2s/rtl/i2s_reg_pkg.sv @@ -35,7 +35,7 @@ package i2s_reg_pkg; typedef struct packed { logic [31:0] q; - logic re; + logic re; } i2s_reg2hw_rxdata_reg_t; typedef struct packed {logic [15:0] q;} i2s_reg2hw_watermark_reg_t; diff --git a/hw/ip/obi_fifo/obi_fifo.core b/hw/ip/obi_fifo/obi_fifo.core index 1e7c41e3c..179f6ee6b 100644 --- a/hw/ip/obi_fifo/obi_fifo.core +++ b/hw/ip/obi_fifo/obi_fifo.core @@ -9,6 +9,8 @@ description: "OBI fifo for peripheral access." filesets: files_rtl: + depend: + - x-heep::packages files: - obi_fifo.sv file_type: systemVerilogSource diff --git a/hw/ip/obi_fifo/obi_fifo.sv b/hw/ip/obi_fifo/obi_fifo.sv index f5216bb51..669ec6434 100644 --- a/hw/ip/obi_fifo/obi_fifo.sv +++ b/hw/ip/obi_fifo/obi_fifo.sv @@ -33,8 +33,8 @@ module obi_fifo producer_obi_req_fsm_e producer_state_n, producer_state_q; typedef struct packed { - logic we; - logic [3:0] be; + logic we; + logic [3:0] be; logic [31:0] addr; logic [31:0] wdata; } obi_data_req_t; diff --git a/hw/ip/obi_spimemio/obi_spimemio.core b/hw/ip/obi_spimemio/obi_spimemio.core index 98efab566..9646969cd 100644 --- a/hw/ip/obi_spimemio/obi_spimemio.core +++ b/hw/ip/obi_spimemio/obi_spimemio.core @@ -7,6 +7,7 @@ filesets: rtl: depend: - yosyshq:picorv32_spimemio:0-r1 + - x-heep::packages files: - rtl/obi_spimemio_reg_pkg.sv - rtl/obi_spimemio_reg_top.sv diff --git a/hw/ip/obi_spimemio/rtl/obi_to_picorv32.sv b/hw/ip/obi_spimemio/rtl/obi_to_picorv32.sv index 9a0e3a7c1..b244082a8 100644 --- a/hw/ip/obi_spimemio/rtl/obi_to_picorv32.sv +++ b/hw/ip/obi_spimemio/rtl/obi_to_picorv32.sv @@ -19,14 +19,14 @@ module obi_to_picorv32 ); typedef enum logic [3:0] { - READ_MEM = 4'b0000, - WRITE_WORD = 4'b1111, + READ_MEM = 4'b0000, + WRITE_WORD = 4'b1111, WRITE_HALFWORD_1 = 4'b1100, WRITE_HALFWORD_0 = 4'b0011, - WRITE_BYTE_3 = 4'b1000, - WRITE_BYTE_2 = 4'b0100, - WRITE_BYTE_1 = 4'b0010, - WRITE_BYTE_0 = 4'b0001 + WRITE_BYTE_3 = 4'b1000, + WRITE_BYTE_2 = 4'b0100, + WRITE_BYTE_1 = 4'b0010, + WRITE_BYTE_0 = 4'b0001 } picorv_request_e; enum logic [1:0] { diff --git a/hw/ip/obi_spimemio/rtl/picorv32_pkg.sv b/hw/ip/obi_spimemio/rtl/picorv32_pkg.sv index 812b733dd..46b15c7b2 100644 --- a/hw/ip/obi_spimemio/rtl/picorv32_pkg.sv +++ b/hw/ip/obi_spimemio/rtl/picorv32_pkg.sv @@ -7,14 +7,14 @@ package picorv32_pkg; typedef struct packed { - logic valid; - logic [3:0] wstrb; + logic valid; + logic [3:0] wstrb; logic [31:0] addr; logic [31:0] wdata; } picorv32_req_t; typedef struct packed { - logic ready; + logic ready; logic [31:0] rdata; } picorv32_resp_t; diff --git a/hw/ip/pdm2pcm/rtl/pdm2pcm.sv b/hw/ip/pdm2pcm/rtl/pdm2pcm.sv index dd3a9e58a..44f570141 100644 --- a/hw/ip/pdm2pcm/rtl/pdm2pcm.sv +++ b/hw/ip/pdm2pcm/rtl/pdm2pcm.sv @@ -59,7 +59,11 @@ module pdm2pcm #( assign rx_data = ({{{32 - FIFO_WIDTH} {1'b0}}, rx_fifo}); - assign hw2reg.status.reach.d = ({{{32-FIFO_ADDR_WIDTH}{1'b0}},fifo_usage}) > {{26{1'b0}},reg2hw.reachcount.q}; + assign hw2reg.status.reach.d = ({ + {{32 - FIFO_ADDR_WIDTH} {1'b0}}, fifo_usage + }) > { + {26{1'b0}}, reg2hw.reachcount.q + }; assign hw2reg.status.reach.de = 1; assign hw2reg.status.fulll.de = 1; assign hw2reg.status.empty.de = 1; diff --git a/hw/ip_examples/iffifo/rtl/iffifo_reg_pkg.sv b/hw/ip_examples/iffifo/rtl/iffifo_reg_pkg.sv index e362798d5..0e9e1d595 100644 --- a/hw/ip_examples/iffifo/rtl/iffifo_reg_pkg.sv +++ b/hw/ip_examples/iffifo/rtl/iffifo_reg_pkg.sv @@ -15,12 +15,12 @@ package iffifo_reg_pkg; typedef struct packed { logic [31:0] q; - logic re; + logic re; } iffifo_reg2hw_fifo_out_reg_t; typedef struct packed { logic [31:0] q; - logic qe; + logic qe; } iffifo_reg2hw_fifo_in_reg_t; typedef struct packed {logic [31:0] q;} iffifo_reg2hw_watermark_reg_t; @@ -53,7 +53,7 @@ package iffifo_reg_pkg; typedef struct packed { logic [31:0] d; - logic de; + logic de; } iffifo_hw2reg_occupancy_reg_t; // Register -> HW type diff --git a/hw/system/pad_control/pad_control.core b/hw/system/pad_control/pad_control.core index 9c39d68e6..960226231 100644 --- a/hw/system/pad_control/pad_control.core +++ b/hw/system/pad_control/pad_control.core @@ -11,6 +11,7 @@ filesets: files_rtl: depend: - pulp-platform.org::common_cells + - x-heep::packages files: - rtl/pad_control_reg_pkg.sv - rtl/pad_control_reg_top.sv diff --git a/hw/system/pad_control/pad_control.vlt b/hw/system/pad_control/pad_control.vlt index 05acd9c2f..b76723454 100644 --- a/hw/system/pad_control/pad_control.vlt +++ b/hw/system/pad_control/pad_control.vlt @@ -6,3 +6,4 @@ lint_off -rule WIDTH -file "*/rtl/pad_control_reg_top.sv" -match "Operator ASSIGNW expects *" lint_off -rule UNDRIVEN -file "*/rtl/pad_control.sv" -match "Signal is not driven: 'pad_muxes_o'*" +lint_off -rule WIDTH -file "*/rtl/pad_control.sv" -match "Operator ASSIGNW expects *" diff --git a/hw/vendor/pulp_platform_axi.core b/hw/vendor/pulp_platform_axi.core new file mode 100644 index 000000000..25c0042eb --- /dev/null +++ b/hw/vendor/pulp_platform_axi.core @@ -0,0 +1,70 @@ +CAPI=2: + +name: pulp-platform.org::axi_heep + +# Copyright 2024 EPFL +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +filesets: + files_rtl: + depend: + - pulp-platform.org::common_cells + files: + - pulp_platform_axi/include/axi/assign.svh : {is_include_file : true, include_path : pulp_platform_axi/include} + - pulp_platform_axi/include/axi/typedef.svh : {is_include_file : true, include_path : pulp_platform_axi/include} + - pulp_platform_axi/src/axi_pkg.sv + # Level 1 + - pulp_platform_axi/src/axi_intf.sv + # Level 2 + - pulp_platform_axi/src/axi_atop_filter.sv + - pulp_platform_axi/src/axi_burst_splitter.sv + - pulp_platform_axi/src/axi_bus_compare.sv + - pulp_platform_axi/src/axi_cdc_dst.sv + - pulp_platform_axi/src/axi_cdc_src.sv + - pulp_platform_axi/src/axi_cut.sv + - pulp_platform_axi/src/axi_delayer.sv + - pulp_platform_axi/src/axi_demux_simple.sv + - pulp_platform_axi/src/axi_dw_downsizer.sv + - pulp_platform_axi/src/axi_dw_upsizer.sv + - pulp_platform_axi/src/axi_fifo.sv + - pulp_platform_axi/src/axi_id_remap.sv + - pulp_platform_axi/src/axi_id_prepend.sv + - pulp_platform_axi/src/axi_isolate.sv + - pulp_platform_axi/src/axi_join.sv + - pulp_platform_axi/src/axi_lite_demux.sv + - pulp_platform_axi/src/axi_lite_dw_converter.sv + - pulp_platform_axi/src/axi_lite_from_mem.sv + - pulp_platform_axi/src/axi_lite_join.sv + - pulp_platform_axi/src/axi_lite_lfsr.sv + - pulp_platform_axi/src/axi_lite_mailbox.sv + - pulp_platform_axi/src/axi_lite_mux.sv + - pulp_platform_axi/src/axi_lite_regs.sv + - pulp_platform_axi/src/axi_lite_to_apb.sv + - pulp_platform_axi/src/axi_lite_to_axi.sv + - pulp_platform_axi/src/axi_modify_address.sv + - pulp_platform_axi/src/axi_mux.sv + - pulp_platform_axi/src/axi_rw_join.sv + - pulp_platform_axi/src/axi_rw_split.sv + - pulp_platform_axi/src/axi_serializer.sv + - pulp_platform_axi/src/axi_slave_compare.sv + - pulp_platform_axi/src/axi_throttle.sv + - pulp_platform_axi/src/axi_to_detailed_mem.sv + # Level 3 + - pulp_platform_axi/src/axi_cdc.sv + - pulp_platform_axi/src/axi_demux.sv + - pulp_platform_axi/src/axi_err_slv.sv + - pulp_platform_axi/src/axi_dw_converter.sv + - pulp_platform_axi/src/axi_from_mem.sv + - pulp_platform_axi/src/axi_id_serialize.sv + - pulp_platform_axi/src/axi_lfsr.sv + - pulp_platform_axi/src/axi_multicut.sv + - pulp_platform_axi/src/axi_to_axi_lite.sv + - pulp_platform_axi/src/axi_to_mem.sv + - pulp_platform_axi/src/axi_zero_mem.sv + file_type : systemVerilogSource + +targets: + default: + filesets: + - files_rtl \ No newline at end of file diff --git a/hw/vendor/pulp_platform_axi.lock.hjson b/hw/vendor/pulp_platform_axi.lock.hjson index c396196f2..748d2f93f 100644 --- a/hw/vendor/pulp_platform_axi.lock.hjson +++ b/hw/vendor/pulp_platform_axi.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/pulp-platform/axi.git - rev: 442ff3375710513623f95944d66cc2bd09b2f155 + rev: 4e54ac6766b160217a83a74d5a23af9bbf59e6ee } } diff --git a/hw/vendor/pulp_platform_axi.vendor.hjson b/hw/vendor/pulp_platform_axi.vendor.hjson index af5c40f98..e6f37444c 100644 --- a/hw/vendor/pulp_platform_axi.vendor.hjson +++ b/hw/vendor/pulp_platform_axi.vendor.hjson @@ -7,7 +7,12 @@ upstream: { url: "https://github.com/pulp-platform/axi.git", - rev: "442ff3375710513623f95944d66cc2bd09b2f155", + rev: "4e54ac6766b160217a83a74d5a23af9bbf59e6ee", }, + exclude_from_upstream: [ + ".github", + "scripts", + "test", + ] } diff --git a/hw/vendor/pulp_platform_axi/.ci/Memora.yml b/hw/vendor/pulp_platform_axi/.ci/Memora.yml index d0e17523d..a4504d448 100644 --- a/hw/vendor/pulp_platform_axi/.ci/Memora.yml +++ b/hw/vendor/pulp_platform_axi/.ci/Memora.yml @@ -83,6 +83,9 @@ artifacts: - src/axi_pkg.sv - src/axi_intf.sv - src/axi_test.sv + - src/axi_err_slv.sv + - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_converter.sv - test/tb_axi_dw_downsizer.sv @@ -97,12 +100,29 @@ artifacts: - src/axi_pkg.sv - src/axi_intf.sv - src/axi_test.sv + - src/axi_err_slv.sv + - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_upsizer.sv - src/axi_dw_converter.sv - test/tb_axi_dw_upsizer.sv outputs: - build/axi_dw_upsizer-%.tested + axi_fifo-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_fifo.sv + - test/tb_axi_fifo.sv + outputs: + - build/axi_fifo-%.tested + + axi_isolate-%: inputs: - Bender.yml @@ -111,11 +131,60 @@ artifacts: - src/axi_pkg.sv - src/axi_intf.sv - src/axi_test.sv + - src/axi_demux.sv + - src/axi_demux_simple.sv + - src/axi_err_slv.sv - src/axi_isolate.sv - test/tb_axi_isolate.sv outputs: - build/axi_isolate-%.tested + axi_iw_converter-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_id_prepend.sv + - src/axi_id_remap.sv + - src/axi_demux.sv + - src/axi_demux_simple.sv + - src/axi_serializer.sv + - src/axi_mux.sv + - src/axi_id_serialize.sv + - src/axi_iw_converter.sv + - test/tb_axi_iw_converter.sv + outputs: + - build/axi_iw_converter-%.tested + + axi_lite_dw_converter-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_lite_dw_converter.sv + - test/tb_axi_lite_dw_converter.sv + outputs: + - build/axi_lite_dw_converter-%.tested + + axi_lite_mailbox-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_lite_mailbox.sv + - test/tb_axi_lite_mailbox.sv + outputs: + - build/axi_lite_mailbox-%.tested + axi_lite_regs-%: inputs: - Bender.yml @@ -155,19 +224,6 @@ artifacts: outputs: - build/axi_lite_to_axi-%.tested - axi_lite_mailbox-%: - inputs: - - Bender.yml - - include - - scripts/run_vsim.sh - - src/axi_pkg.sv - - src/axi_intf.sv - - src/axi_test.sv - - src/axi_lite_mailbox.sv - - test/tb_axi_lite_mailbox.sv - outputs: - - build/axi_lite_mailbox-%.tested - axi_lite_xbar-%: inputs: - Bender.yml @@ -239,6 +295,23 @@ artifacts: outputs: - build/axi_to_axi_lite-%.tested + axi_to_mem_banked-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_demux.sv + - src/axi_demux_simple.sv + - src/axi_to_detailed_mem.sv + - src/axi_to_mem.sv + - src/axi_to_mem_banked.sv + - test/tb_axi_to_mem_banked.sv + outputs: + - build/axi_to_mem_banked-%.tested + axi_xbar-%: inputs: - Bender.yml @@ -248,8 +321,11 @@ artifacts: - src/axi_intf.sv - src/axi_test.sv - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_err_slv.sv - src/axi_mux.sv + - src/axi_multicut.sv + - src/axi_xbar_unmuxed.sv - src/axi_xbar.sv - test/tb_axi_xbar.sv outputs: diff --git a/hw/vendor/pulp_platform_axi/.github/workflows/doc.yml b/hw/vendor/pulp_platform_axi/.github/workflows/doc.yml deleted file mode 100644 index dfda93be7..000000000 --- a/hw/vendor/pulp_platform_axi/.github/workflows/doc.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Build and deploy documentation -on: - push: - branches-ignore: - - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - - v** # such branch names conflict with tags - tags: - - v** - pull_request: - branches-ignore: - - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - - v** # such branch names conflict with tags - -jobs: - build-and-deploy: - if: github.repository == 'pulp-platform/axi' # do not run this job on forks (because deployment - runs-on: ubuntu-latest # will fail) - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - persist-credentials: false - - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ubuntu-latest-cargo-registry-${{ hashFiles('.github/workflows/doc.yml') }} - - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ubuntu-latest-cargo-index-${{ hashFiles('.github/workflows/doc.yml') }} - - - name: Cache cargo binaries - uses: actions/cache@v1 - with: - path: ~/.cargo/bin - key: ubuntu-latest-cargo-binaries-${{ hashFiles('.github/workflows/doc.yml') }} - - - name: Install Bender and Morty - run: | - rustup update stable --no-self-update && rustup default stable - if ! $(which bender); then - cargo install bender --version 0.18.0 - fi - if ! $(which morty); then - cargo install --git https://github.com/zarubaf/morty --rev 4855119c1378d45d9ac35cfa525725d2786e68f3 - fi - shell: bash - - - name: Build documentation - run: | - mkdir -p docs - morty -I include -I $(bender path common_cells)/include src/*.sv -d docs - shell: bash - - - name: Determine documentation target folder - run: | - if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then - DOC_TARGET="$GITHUB_HEAD_REF" - elif [ "$GITHUB_EVENT_NAME" == "push" ]; then - if echo $GITHUB_REF | grep -qE '^refs/(head|tag)s'; then - DOC_TARGET="$(echo $GITHUB_REF | cut -d '/' -f3-)" - else - echo "Error: Could not derive documentation target folder for ref '$GITHUB_REF'!" - exit 1 - fi - else - echo "Error: Unsupported event: '$GITHUB_EVENT_NAME'!" - exit 1 - fi - echo "DOC_TARGET=$DOC_TARGET" >> $GITHUB_ENV - - - name: Deploy documentation - uses: JamesIves/github-pages-deploy-action@releases/v3 - if: > - github.event_name == 'push' - || github.event.pull_request.head.repo.full_name == github.repository - with: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: docs # The folder the action should deploy. - TARGET_FOLDER: ${{ env.DOC_TARGET }} - CLEAN: true # remove files from `TARGET_FOLDER` that are not in `FOLDER` - # (`rsync --delete`) diff --git a/hw/vendor/pulp_platform_axi/.github/workflows/gitlab-ci.yml b/hw/vendor/pulp_platform_axi/.github/workflows/gitlab-ci.yml deleted file mode 100644 index 4118a2a44..000000000 --- a/hw/vendor/pulp_platform_axi/.github/workflows/gitlab-ci.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Retrieve CI result from GitLab -on: - push: - branches-ignore: - - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - - v** # such branch names conflict with tags - pull_request: - branches-ignore: - - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - - v** # such branch names conflict with tags - -jobs: - gitlab-ci: - if: github.repository == 'pulp-platform/axi' # do not run this job on forks (because Gitlab CI - runs-on: ubuntu-latest # will not trigger on forks) - timeout-minutes: 190 - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - persist-credentials: false - # Checkout pull request HEAD commit instead of merge commit, because CI runs against HEAD - # commit. - ref: ${{ github.event.pull_request.head.sha }} - - - name: Wait for synchronization (every 5 minutes) - run: | - while [ $(($(date -d "+1 minute" +%-M) % 5)) -ne 0 ]; do - # "+1 minute" because if the current minute is divisible by 5, we likely already missed - # the synchronization. - sleep 10 - done - sleep 90 # the minute above plus 30 seconds to leave some time for the synchronization - shell: bash - - - name: Obtain CI result - run: | - if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then - BRANCH_NAME="$GITHUB_HEAD_REF" - elif [ "$GITHUB_EVENT_NAME" == "push" ]; then - if echo $GITHUB_REF | grep -qE '^refs/heads'; then - BRANCH_NAME="$(echo $GITHUB_REF | cut -d '/' -f3-)" - else - echo "Error: Could not derive branch name from ref '$GITHUB_REF'!" - exit 1 - fi - else - echo "Error: Unsupported event: '$GITHUB_EVENT_NAME'!" - exit 1 - fi - while true; do - resp="$(curl --fail --silent --show-error \ - https://akurth.net/usrv/ig/shields/pipeline/akurth/axi/$BRANCH_NAME)" - if [ $? -ne 0 ]; then - echo "Error: Failed to obtain CI status!" - exit 1 - fi - status="$(echo $resp | jq -r .message)" - if [ "$status" == "passing" ]; then - sha="$(echo $resp | jq -r .sha)" - if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then - github_sha="$(cat "$GITHUB_EVENT_PATH" | jq -r .pull_request.head.sha)" - else - github_sha="$GITHUB_SHA" - fi - if [ "$sha" == "$github_sha" ]; then - echo "CI passed." - exit 0 - else - echo "Error: CI passed, but on a different SHA: '$sha'!" - exit 1 - fi - elif [ "$status" == "running" ]; then - echo "CI is running, waiting .." - else - echo "Error: Unknown or failing status: '$status'!" - exit 1 - fi - sleep 10 - done - shell: bash diff --git a/hw/vendor/pulp_platform_axi/.gitignore b/hw/vendor/pulp_platform_axi/.gitignore index c9eb4f507..10c4aecdd 100644 --- a/hw/vendor/pulp_platform_axi/.gitignore +++ b/hw/vendor/pulp_platform_axi/.gitignore @@ -5,3 +5,5 @@ /build /Bender.lock /Bender.local +*.log +*.wlf diff --git a/hw/vendor/pulp_platform_axi/.gitlab-ci.yml b/hw/vendor/pulp_platform_axi/.gitlab-ci.yml index 45335913d..1ac12d887 100644 --- a/hw/vendor/pulp_platform_axi/.gitlab-ci.yml +++ b/hw/vendor/pulp_platform_axi/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - SYNOPSYS_DC: synopsys-2019.12 dc_shell -64bit + SYNOPSYS_DC: synopsys-2022.03 dcnxt_shell before_script: - export PATH=~/.cargo/bin:$PATH @@ -28,7 +28,7 @@ vsim: fi parallel: matrix: - - VSIM_VER: ['10.7b', '10.7e', '2020.1', '2021.1'] + - VSIM_VER: ['10.7b', '2022.3'] synopsys_dc: stage: build @@ -39,6 +39,18 @@ synopsys_dc: $CI_PROJECT_DIR/.gitlab-ci.d/memora_retry.sh insert synopsys_dc fi +fuse_xsim: + stage: build + allow_failure: true + timeout: 5 minutes + script: + - bender sources + - /usr/local/anaconda3/bin/python3 -m pip install fusesoc --user + - mkdir fusesoc && cd fusesoc + - $HOME/.local/bin/fusesoc library add axi .. + - $HOME/.local/bin/fusesoc core list + - vitis-2022.1-zr $HOME/.local/bin/fusesoc run --tool xsim --target sim --no-export pulp-platform.org::axi:$(cat ../VERSION | sed s/-/./g) + .run_vsim: &run_vsim stage: test script: @@ -61,7 +73,7 @@ synopsys_dc: fi parallel: matrix: - - VSIM_VER: ['10.7b', '10.7e', '2020.1', '2021.1'] + - VSIM_VER: ['10.7b', '2022.3'] axi_addr_test: <<: *run_vsim @@ -93,11 +105,31 @@ axi_dw_upsizer: variables: TEST_MODULE: axi_dw_upsizer +axi_fifo: + <<: *run_vsim + variables: + TEST_MODULE: axi_fifo + axi_isolate: <<: *run_vsim variables: TEST_MODULE: axi_isolate +axi_iw_converter: + <<: *run_vsim + variables: + TEST_MODULE: axi_iw_converter + +axi_lite_dw_converter: + <<: *run_vsim + variables: + TEST_MODULE: axi_lite_dw_converter + +axi_lite_mailbox: + <<: *run_vsim + variables: + TEST_MODULE: axi_lite_mailbox + axi_lite_regs: <<: *run_vsim variables: @@ -113,11 +145,6 @@ axi_lite_to_axi: variables: TEST_MODULE: axi_lite_to_axi -axi_lite_mailbox: - <<: *run_vsim - variables: - TEST_MODULE: axi_lite_mailbox - axi_lite_xbar: <<: *run_vsim variables: @@ -143,6 +170,11 @@ axi_to_axi_lite: variables: TEST_MODULE: axi_to_axi_lite +axi_to_mem_banked: + <<: *run_vsim + variables: + TEST_MODULE: axi_to_mem_banked + axi_xbar: <<: *run_vsim variables: diff --git a/hw/vendor/pulp_platform_axi/Bender.yml b/hw/vendor/pulp_platform_axi/Bender.yml index 78730b072..f30fcff2d 100644 --- a/hw/vendor/pulp_platform_axi/Bender.yml +++ b/hw/vendor/pulp_platform_axi/Bender.yml @@ -1,15 +1,27 @@ package: name: axi authors: - - "Andreas Kurth " # current maintainer + # Alphabetically ordered by last name (maintainers first) + - "Thomas Benz " # current maintainer + - "Michael Rogenmoser " # current maintainer + - "Matheus Cavalcante " + - "Tim Fischer " + - "Noah Huetter " + - "Cyril Koenig " + - "Andreas Kurth " + - "Stefan Mach " + - "Samuel Riedel " + - "Wolfgang Rönninger " + - "Paul Scheffler " - "Fabian Schuiki " + - "Luca Valente " + - "Nils Wistoff " - "Florian Zaruba " - - "Matheus Cavalcante " - - "Wolfgang Roenninger " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 } - common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.37.0 } + common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } + tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } export_include_dirs: - include @@ -25,18 +37,24 @@ sources: # Level 2 - src/axi_atop_filter.sv - src/axi_burst_splitter.sv + - src/axi_bus_compare.sv - src/axi_cdc_dst.sv - src/axi_cdc_src.sv - src/axi_cut.sv - src/axi_delayer.sv - - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv + - src/axi_fifo.sv + - src/axi_id_remap.sv - src/axi_id_prepend.sv - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv + - src/axi_lite_from_mem.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -44,16 +62,36 @@ sources: - src/axi_lite_to_axi.sv - src/axi_modify_address.sv - src/axi_mux.sv + - src/axi_rw_join.sv + - src/axi_rw_split.sv - src/axi_serializer.sv + - src/axi_slave_compare.sv + - src/axi_throttle.sv + - src/axi_to_detailed_mem.sv # Level 3 - src/axi_cdc.sv + - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv + - src/axi_from_mem.sv + - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv + - src/axi_to_mem.sv + - src/axi_zero_mem.sv # Level 4 + - src/axi_interleaved_xbar.sv + - src/axi_iw_converter.sv - src/axi_lite_xbar.sv + - src/axi_xbar_unmuxed.sv + - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv + # Level 5 - src/axi_xbar.sv + # Level 6 + - src/axi_xp.sv - target: synth_test files: @@ -61,6 +99,8 @@ sources: - target: simulation files: + - src/axi_chan_compare.sv + - src/axi_dumper.sv - src/axi_sim_mem.sv - src/axi_test.sv @@ -72,18 +112,24 @@ sources: # Level 1 - test/tb_axi_addr_test.sv - test/tb_axi_atop_filter.sv + - test/tb_axi_bus_compare.sv - test/tb_axi_cdc.sv - test/tb_axi_delayer.sv - test/tb_axi_dw_downsizer.sv - test/tb_axi_dw_upsizer.sv + - test/tb_axi_fifo.sv - test/tb_axi_isolate.sv + - test/tb_axi_lite_dw_converter.sv - test/tb_axi_lite_mailbox.sv - test/tb_axi_lite_regs.sv + - test/tb_axi_iw_converter.sv - test/tb_axi_lite_to_apb.sv - test/tb_axi_lite_to_axi.sv - test/tb_axi_lite_xbar.sv - test/tb_axi_modify_address.sv - test/tb_axi_serializer.sv - test/tb_axi_sim_mem.sv + - test/tb_axi_slave_compare.sv - test/tb_axi_to_axi_lite.sv + - test/tb_axi_to_mem_banked.sv - test/tb_axi_xbar.sv diff --git a/hw/vendor/pulp_platform_axi/CHANGELOG.md b/hw/vendor/pulp_platform_axi/CHANGELOG.md index 8133aea9d..ee650d9b5 100644 --- a/hw/vendor/pulp_platform_axi/CHANGELOG.md +++ b/hw/vendor/pulp_platform_axi/CHANGELOG.md @@ -5,6 +5,339 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed +- Disabled the interface variant of `axi_xbar_unmuxed` for VCS, as VCS does not support multi-dimensional arrays of interfaces yet. + +## 0.39.4 - 2024-07-25 +### Added +- `axi_sim_mem`: Increase number of request ports, add multiport interface variant. +- `axi_bus_compare`: Optionally consider AXI `size` field to only compare used data. +- `AXI_BUS_DV`: Add property checking that bursts do not cross 4KiB page boundaries. +- Add `axi_xbar_unmuxed`: Partial crossbar with unmultiplexed mst_ports. + +### Fixed +- `axi_bus_compare`: Fix mismatch detection. +- `axi_to_detailed_mem`: Only respond with `exokay` if `lock` was set on the request. + Bump `common_cells` for `mem_to_banks` fix. +- `axi_dw_downsizer`: Fix `i_forward_b_beats_queue` underflow. +- `axi_atop_filter`: Add reset state to internal FSM to avoid simulation bug in XSIM. +- `axi_test`: Ensure random requests do not cross 4KiB page boundaries. + +### Changed +- `axi_id_serializer`: Change internal design (and behavior) for simpler code, less hardware, and + less stalling. + +`v0.39.4` is fully **backward-compatible** to `v0.39.3`. + +## 0.39.3 - 2024-05-08 +### Added +- `axi_sim_mem`: Allow response data for uninitialized region to have configurable defined value. +- `axi_test`: add `clear_memory_regions` to `axi_rand_master`. +- `axi_test`: Add `add_traffic_shaping_with_size` to `axi_rand_master` to allow for traffic shaping + with a custom size. + +### Changed +- `axi_pkg`: Adjust `LatencyMode` parameter of `xbar_cfg_t` to bit vector from `xbar_pipeline_e` + enum to allow custom configurations. + +`v0.39.3` is fully **backward-compatible** to `v0.39.2`. + +## 0.39.2 - 2024-03-13 + +### Added +- `axi_interleaved_xbar`: An experimental crossbar extension interleaving memory transfers over #334 + subordinate devices. ***Use at your own risk***. +- `axi_zero_mem`: Implementing *\dev\zero* function for AXI. #334 + +### Fixed +- `axi_to_detailed_mem`: VCS crashed on default parameters 0, changed them to 1 #334 +- `axi_to_mem`: Add missing testmode pins #327 +- `axi_sim_mem`: Fix byte calculation in R and W forks #331 + +`v0.39.2` is fully **backward-compatible** to `v0.39.1`. + +## 0.39.1 - 2023-09-05 + +### Added +- `axi_cdc`: Add `SyncStages` parameter. +- `axi_to_mem_interleaved`: Add interface variant. +- `axi_burst_splitter`: Expose `id_queue`'s `FULL_BW` parameter. +- `axi_chan_compare`: Add parameter to allow reordered transactions. +- Add `AXI_HIGHLIGHT` macro to highlight AXI signals. +- Add flat port instantiation macros. + +### Fixed +- `axi_test`: Avoid false negatives for misaligned reads in `axi_scoreboard`. +- `axi_to_detailed_mem`: Ensure proper propagation or `err` and `exokay` signals. + +## 0.39.0 - 2023-07-20 + +### Added +- Synthesizable IPs: + - `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used + to compare two AXI buses on an FPGA. + - `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. + - `axi_lite_dw_converter`: Convert the data width of AXI4-Lite transactions. Emits the + appropriate amount of downstream transactions to perform the whole requested access. + - `axi_rw_join` and `axi_rw_split` to split/join the read and write channels of an AXI bus. +- `CT`-macros allowing to instantiate AXI structs with custom channel type names. +- `axi_pkg': Add documentation to `xbar_cfg_t`. +- Testbench IPs: + - `axi_chan_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type + - Add `axi_file_master` to `axi_test`, allowing file-based AXI verification approaches. + - Add `#_width` functions to `axi_test` returning the width of the AXI channels. + +### Changed +- Synthesizable IPs: + - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents + AWs from being issued to one master port while Ws from another burst are ongoing to another + master port. This is required to prevents deadlocks due to circular waits downstream. Removes + `FallThrough` parameter from `axi_demux`. + - Split the `axi_demux` logic and timing decoupling. A new module called `axi_demux_simple` contains + the core logic. + - `axi_dw_downsizer` uses `axi_pkg::RESP_EXOKAY` as a default value. + - Simplify the `casez` in `axi_id_remap`. + - Add optional explicit mapping to the `axi_id_serialize` module. + - Expand `axi_to_mem` to `axi_to_detailed_mem` exposing all of AXI's side-signals; namely `id`, `user`, + `cache`, `prot`, `qos`, `region`, `atop`. Add possibility to inject `err` and `exokay`. + - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the `xbar` between the *demuxes* and *muxes*. Improve inline + documentation. + - Move `mem_to_banks` to `common_cells`. +- `axi_pkg`: Improve for better compatibility with *Vivado*. +- `axi_test: + - `axi_lite_rand_slave`: `R` response field is now randomized. + - Remove excessive prints from random master and slave. + - Properly size-align the address. +- `axi_pkg`: Define `localparams` to define AXI type widths. +- Update `common_cells` from version `v1.26.0` to `v1.27.0`. +- Tooling: + - Use `pulp-platform/pulp-actions/gitlab-ci@v2` in the GitHub CI to communicate with the internal CI. + - Bump `DC Shell version` from `2019.12` to `2022.03` + - No longer check *ModelSim* versions `10.7e` and `2021.3`, add `2022.3`. + - More thorough verification runs for the `xbar`. + - Start transitioning from shell script to Makefile to run simulations. +- Use `scripts/update_authors` to update authors, slight manual fixes performed. + +### Fixed +- `axi_to_mem_banked`: Reduce hardware by properly setting `UniqueIds`. +- `axi_to_mem_interleaved` and `axi_to_mem_split` properly instantiates a demultiplexer now. + Adds `test_i` port for DFT. + +### Breaking Changes +There are breaking changes between `v0.38.0` and `v0.39.0`: +- `axi_demux`: `FallThrough` parameter was removed. +- `axi_xbar`: `axi_pkg::xbar_cfg_t` added `PipelineStages` parameter. +- `axi_to_mem_interleaved` and `axi_to_mem_split`: Added `test_i` input port. + +## 0.38.0 - 2022-09-28 + +### Added +- Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging + purposes. +- Add FuseSoC and Vivado XSIM limited test to CI +- `assign.svh`: Add macros to assign flat buses using the Vivado naming style. +- `axi_lfsr` and `axi_lite_lfsr`: Add AXI4 and AXI4 Lite LFSR Subordinate devices. +- `axi_xp`: Add crosspoint with homomorphous slave and master ports. + +### Changed +- Improve compatibility with FuseSoC +- Improve compatibility with Vivado XSIM +- Performance improvements to `axi_to_mem` +- Use `scripts/update_authors` to update authors, slight manual fixes performed. + +`v0.38.0` is fully **backward-compatible** to `v0.36.0` and `v0.37.0`. + + +## 0.37.0 - 2022-08-30 + +### Added +- `axi_fifo`: Inserts a FIFO into all 5 AXI4 channels; add module and its testbench +- `axi_test`: Add `mapped` mode to the random classes as well as additional functionality to the + scoreboard class. +- `axi_throttle`: Add a module that limits the maximum number of outstanding transfers sent to the + downstream logic. +- `axi_to_mem`: AXI4+ATOP slave to control on chip memory. +- `axi_to_mem_banked`: AXI4+ATOP slave to control on chip memory, with banking support, higher + throughput than `axi_to_mem`. +- `axi_to_mem_interleaved`: AXI4+ATOP slave to control on chip memory, interleaved to prevent + deadlocks. +- `axi_to_mem_split`: AXI4+ATOP slave to control memory protocol interconnect. +- `Bender`: Add dependency `tech_cells_generic` `v0.2.2` for generic SRAM macro for simulation. + +### Changed +- `axi_demux`: Add module docstring +- `axi_sim_mem`: Add the capability to emit read and write errors +- `Bender`: Update dependency `common_cells` to `v1.26.0` from `v1.21.0` (required by + `axi_throttle`) +- Remove `docs` directory, move content to `doc` folder. `docs` is automatically created and + populated during the CI run. +- Update vsim version to `2021.3` in CI, drop test for `2020.1` and `2021.1` + +### Fixed +- `axi_lite_demux`: Improve compatibility with vsim version 10.7b. +- `axi_lite_mux`: Reduce complexity of W channel at master port by removing an unnecessary + multiplexer. + +`v0.37.0` is fully **backward-compatible** to `v0.36.0`. + + +## 0.36.0 - 2022-07-07 + +### Added +- Add Monitor modport to `AXI_BUS`, `AXI_LITE`, and `AXI_LITE_DV` interfaces. + + +## 0.35.3 - 2022-05-03 + +### Fixed +- `axi_demux`: Eliminate unnecessary stalls of AW channel when the AR channel has reached its + maximum number of transactions. Prior to this fix, `axi_demux` would always stall AWs while read + transactions were at their maximum (that is, while `MaxTrans` read transactions were outstanding). + However, this stall is only required when the AW that is being handled by `axi_demux` is an atomic + operation (ATOP) that entails an R response. This fix therefore removes unnecessary stalls as + well as an unnecessary dependency between reads and writes. The integrity of data or transactions + was not affected by this problem. + + +## 0.35.2 - 2022-04-14 + +### Fixed +- `axi_lite_mux_intf`: Fix type of `slv` and `mst` interface ports; they were `AXI_BUS` instead of + `AXI_LITE`. +- `axi_xbar_intf`: Fix order of parameters. Prior to this fix, the `CONNECTIVITY` parameter was + defined using the `Cfg` parameter before the `Cfg` parameter was defined. +- `axi_test::axi_rand_master`: Improve compatibility with simulators by changing an implication + inside an assertion to a conditional assertion. + + +## 0.35.1 - 2022-03-31 + +### Fixed +- `axi_demux` and `axi_lite_demux`: Add missing spill registers for configurations with a single + master port. +- `axi_demux_intf`: Add missing parameter (`ATOP_SUPPORT`) to optionally disable support for atomic + operations. +- `axi_mux` and `axi_lite_mux`: Add missing spill registers for configurations with a single slave + port. +- `axi_lite_mux_intf`: Add missing parameter values on the internal `axi_lite_mux` instance + (`axi_req_t` and `axi_resp_t`). +- `axi_sim_mem`: Propagate the AR channel's user signal correctly to the monitor. + + +## 0.35.0 - 2022-03-11 + +### Added +- `axi_sim_mem`: Add monitoring interface to observe the point of coherency between the write and + the read channel. + +### Fixed +- `axi_sim_mem`: Keep R response stable while not accepted. + + +## 0.34.0 - 2022-03-09 + +### Added +- `axi_demux` and `axi_isolate`: Add parameter `AtopSupport` to optionally disable the support for + atomic operations (ATOPs). This parameter defaults to `1'b1`, i.e., ATOPs are supported. + Therefore, this change is backward-compatible. +- `axi_isolate`: Add parameter `TerminateTransaction` to optionally respond to transactions during + isolation. This parameter defaults to `1'b0`, i.e., transactions do not get responses. + Therefore, this change is backward-compatible. +- `axi_xbar`: Add `Connectivity` parameter to enable the implementation of partially-connected + crossbars. This parameter defaults to `'1`, i.e., every slave port is connected to every master + port. Therefore, this change is backward-compatible. +- `axi_test`: Add monitor class `axi_monitor`. +- `axi_test::axi_driver`: Add monitor tasks. + +### Changed +- `axi_isolate`: Add parameters for the address, data, ID, and user signal width. This is required + for the implementation of the `TerminateTransaction` parameter (see *Added* section). This change + is **backward-incompatible** for all instances of `axi_isolate` outside this repository. Users + must update all instances of `axi_isolate` in their code. The interface variant is not affected + and remains backward-compatible. + + +## 0.33.1 - 2022-02-26 + +### Fixed +- `axi_xbar_intf`: Add missing `ATOPS` parameter to optionally disable the support of atomic + operations (introduced in v0.25.0 for `axi_xbar`). The default value of the added parameter makes + this fix backward-compatible. + + +## 0.33.0 - 2022-02-21 + +### Added +- Add `axi_sim_mem_intf` interface variant of `axi_sim_mem`. + +### Fixed +- `axi_cdc`: Improve compatibility with VCS by restricting a QuestaSim workaround to be used only + for QuestaSim (issue #207). +- `axi_id_remap`: Improve compatibility with Verilator by excluding `assert`s for that tool. +- `axi_lite_demux`: Improve compatibility with VCS (issue #187 reported for `axi_demux`, which was + fixed in v0.29.2). +- `axi_xbar`: Improve compatibility with VCS by adding VCS-specific code that does not use constant + function calls (#208). + + +## 0.32.0 - 2022-01-25 + +### Changed +- `axi_atop_filter`, `axi_burst_splitter`, `axi_cut`, `axi_delayer`, `axi_demux`, `axi_err_slv`, + `axi_isolate`, `axi_lite_demux`, `axi_lite_mux`, `axi_lite_to_axi`, and `axi_lite_xbar`, + `axi_multicut`, `axi_serializer`, `axi_sim_mem`: Prefix `req_t` and `resp_t` type parameters with + `axi_`. This prevents type collisions in tools that have problems with correct type resolution + and isolation. This change is **backward-incompatible** for all instances of the listed modules + outside this repository. Users must update all instances of the listed modules in their code. + Interface variants are not affected and remain backward-compatible. + + +## 0.31.1 - 2022-01-17 + +### Fixed +- `axi_xbar`: Fix signal width for single master port. Before this fix, a crossbar instantiated + with a single master port would contain arrays with incorrect dimensions. + + +## 0.31.0 - 2021-12-07 + +### Added +- Add three modules to convert between any two AXI ID widths under many different concurrency + requirements: + - `axi_iw_converter` is the top-level module that converts between any two AXI ID widths with all + supported parameters. It upsizes IDs by extending the MSBs with zeros and joins two interfaces + with identical ID widths. For downsizing IDs, it instantiates one of the following two modules: + - `axi_id_remap` remaps AXI IDs from wide IDs at the slave port to narrower IDs at the master + port without serializing transactions. + - `axi_id_serialize` reduces AXI IDs by serializing transactions when necessary. + + +## 0.30.0 - 2021-12-01 + +### Added +- Add `axi_lite_xbar_intf` interface variant of `axi_lite_xbar`. + +### Fixed +- `axi_lite_demux`: Improve compatibility with new version of QuestaSim's optimizer (`vopt`). + Before this workaround, QuestaSim 2021.1 could segfault on instances of `axi_lite_demux`. + + +## 0.29.2 - 2021-11-12 + +### Fixed +- `axi_demux`: Improve compatibility with VCS (#187). The workaround of #169 was not compatible + with VCS 2020.12. That workaround is now only active if `TARGET_VSIM` is defined. +- `axi_dw_downsizer` and `axi_dw_upsizer` (part of `axi_dw_converter`): Avoid latch inference on the + Mentor Precision synthesis tool. +- `axi_lite_cdc_src_intf`: Fix `_i` and `_o` suffixes in instantiation of `axi_cdc_src`. +- `axi_test::axi_rand_slave`: Improve compatibility with VCS (#175). +- `axi_test::axi_scoreboard`: Add default value to parameters to improve compatibility with some + tools. + + ## 0.29.1 - 2021-06-02 ### Fixed diff --git a/hw/vendor/pulp_platform_axi/Makefile b/hw/vendor/pulp_platform_axi/Makefile new file mode 100644 index 000000000..5d97a9542 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/Makefile @@ -0,0 +1,90 @@ +# Copyright and related rights are licensed under the Solderpad Hardware +# License, Version 0.51 (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +# or agreed to in writing, software, hardware and materials distributed under +# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# Authors: +# - Thomas Benz + +# select IIS-internal tool commands if we run on IIS machines +ifneq (,$(wildcard /etc/iis.version)) + VSIM ?= questa-2022.3 vsim + SYNOPSYS_DC ?= synopsys-2022.03 dcnxt_shell +else + VSIM ?= vsim + SYNOPSYS_DC ?= dc_shell +endif + +TBS ?= axi_addr_test \ + axi_atop_filter \ + axi_cdc axi_delayer \ + axi_dw_downsizer \ + axi_dw_upsizer \ + axi_fifo \ + axi_isolate \ + axi_iw_converter \ + axi_lite_regs \ + axi_lite_to_apb \ + axi_lite_to_axi \ + axi_lite_mailbox \ + axi_lite_xbar \ + axi_modify_address \ + axi_serializer \ + axi_sim_mem \ + axi_to_axi_lite \ + axi_to_mem_banked \ + axi_xbar + +SIM_TARGETS := $(addsuffix .log,$(addprefix sim-,$(TBS))) + + +.SHELL: bash + +.PHONY: help all sim_all clean + + +help: + @echo "" + @echo "elab.log: elaborates all files using Synopsys DC" + @echo "compile.log: compile files using Questasim" + @echo "sim-#TB#.log: simulates a given testbench, available TBs are:" + @echo "$(addprefix ###############-#,$(TBS))" | sed -e 's/ /\n/g' | sed -e 's/#/ /g' + @echo "sim_all: simulates all available testbenches" + @echo "" + @echo "clean: cleans generated files" + @echo "" + + +all: compile.log elab.log sim_all + + +sim_all: $(SIM_TARGETS) + + +build: + mkdir -p $@ + + +elab.log: Bender.yml | build + export SYNOPSYS_DC="$(SYNOPSYS_DC)"; cd build && ../scripts/synth.sh | tee ../$@ + (! grep -n "Error:" $@) + + +compile.log: Bender.yml | build + export VSIM="$(VSIM)"; cd build && ../scripts/compile_vsim.sh | tee ../$@ + (! grep -n "Error:" $@) + + +sim-%.log: compile.log + export VSIM="$(VSIM)"; cd build && ../scripts/run_vsim.sh --random-seed $* | tee ../$@ + (! grep -n "Error:" $@) + (! grep -n "Fatal:" $@) + + +clean: + rm -rf build + rm -f *.log diff --git a/hw/vendor/pulp_platform_axi/README.md b/hw/vendor/pulp_platform_axi/README.md index 96d33fccf..6f798139e 100644 --- a/hw/vendor/pulp_platform_axi/README.md +++ b/hw/vendor/pulp_platform_axi/README.md @@ -1,56 +1,84 @@ # AXI SystemVerilog Modules for High-Performance On-Chip Communication -[![CI status](https://akurth.net/usrv/ig/shields/pipeline/akurth/axi/master.svg)](https://iis-git.ee.ethz.ch/akurth/axi/commits/master) +[![CI status](https://github.com/pulp-platform/axi/actions/workflows/gitlab-ci.yml/badge.svg?branch=master)](https://github.com/pulp-platform/axi/actions/workflows/gitlab-ci.yml?query=branch%3Amaster) [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/pulp-platform/axi?color=blue&label=current&sort=semver)](CHANGELOG.md) [![SHL-0.51 license](https://img.shields.io/badge/license-SHL--0.51-green)](LICENSE) -This repository provides modules to build on-chip communication networks adhering to the [AXI4 or AXI4-Lite standards](https://developer.arm.com/documentation/ihi0022/f-b). For high-performance communication, we implement AXI4[+ATOPs from AXI5](#atomic-operations). For lightweight communication, we implement AXI4-Lite. We aim to provide a complete end-to-end communication platform, including endpoints such as DMA engines and on-chip memory controllers. +This repository provides modules to build on-chip communication networks adhering to the [AXI4 or AXI4-Lite standards][AMBA 5 Spec]. For high-performance communication, we implement AXI4[+ATOPs from AXI5](#atomic-operations). For lightweight communication, we implement AXI4-Lite. We aim to provide a complete end-to-end communication platform, including endpoints such as DMA engines and on-chip memory controllers. Our **design goals** are: - **Topology Independence**: We provide elementary building blocks such as protocol [multiplexers](src/axi_mux.sv) and [demultiplexers](src/axi_demux.sv) that allow users to implement any network topology. We also provide commonly used interconnecting components such as a [crossbar](src/axi_xbar.sv). - **Modularity**: We favor design by composition over design by configuration where possible. We strive to apply the *Unix philosophy* to hardware: make each module do one thing well. This means you will more often instantiate our modules back-to-back than change a parameter value to build more specialized networks. -- **Fit for Heterogeneous Networks**: Our modules are parametrizable in terms of data width and transaction concurrency. This allows to create optimized networks for a wide range of performance (e.g., bandwidth, concurrency, timing), power, and area requirements. We provide modules such as [data width converters](src/axi_dw_converter.sv) that allow to join subnetworks with different properties, creating heterogeneous on-chip networks. +- **Fit for Heterogeneous Networks**: Our modules are parametrizable in terms of data width and transaction concurrency. This allows to create optimized networks for a wide range of performance (e.g., bandwidth, concurrency, timing), power, and area requirements. We provide modules such as [data width converters](src/axi_dw_converter.sv) and [ID width converters](src/axi_iw_converter.sv) that allow to join subnetworks with different properties, creating heterogeneous on-chip networks. - **Full AXI Standard Compliance**. - **Compatibility** with a [wide range of (recent versions of) EDA tools](#which-eda-tools-are-supported) and implementation in standardized synthesizable SystemVerilog. -The **design and microarchitecture** of the modules in this repository is described in [**this paper**](https://arxiv.org/abs/2009.05334). If you use our work in your research, please cite it. +The **design and microarchitecture** of the modules in this repository is described in [**this paper**](https://ieeexplore.ieee.org/document/9522037) ([preprint](https://arxiv.org/pdf/2009.05334)). If you use our work in your research, please cite it. ## List of Modules In addition to the documents linked in the following table, we are setting up [documentation auto-generated from inline docstrings](https://pulp-platform.github.io/axi/master). (Replace `master` in that URL with a tag to get the documentation for a specific version.) -| Name | Description | Doc | -|------------------------------------------------------|---------------------------------------------------------------------------------------------------|--------------------------------| -| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | -| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | -| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | -| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | -| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | -| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | -| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | -| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | -| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | -| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | -| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | -| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | -| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | -| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | -| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | -| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | -| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | -| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | -| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | -| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | -| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | -| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | -| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | -| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | -| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | -| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | -| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | -| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | -| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | -| [`axi_xbar`](src/axi_xbar.sv) | Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | +| Name | Description | Doc | +|---------------------------------------------------------|------------------------------------------------------------------------------------------------------|----------------------------------| +| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | +| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | +| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | +| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | +| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | +| [`axi_demux_simple`](src/axi_demux_simple.sv) | Demux without spill registers. | [Doc](doc/axi_demux.md) | +| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | +| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | +| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | +| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | +| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | +| [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | +| [`axi_from_mem`](src/axi_from_mem.sv) | This module acts like an SRAM and makes AXI4 requests downstream. | | +| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | +| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | +| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | +| [`axi_interleaved_xbar`](src/axi_interleaved_xbar.sv) | Interleaved version of the crossbar. This module is experimental; use at your own risk. | | +| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | +| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | +| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | +| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | +| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | +| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | +| [`axi_lite_dw_converter`](src/axi_lite_dw_converter.sv) | A data width converter between two AXI-Lite busses | [Doc][doc.axi_lite_dw_converter] | +| [`axi_lite_from_mem`](src/axi_lite_from_mem.sv) | This module acts like an SRAM and makes AXI4-Lite requests downstream. | | +| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | +| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | +| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | +| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | +| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | +| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | +| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | +| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | +| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | +| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | +| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | +| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | +| [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | +| [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | +| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | +| [`axi_slave_compare`](src/axi_slave_compare.sv) | Compares two slave devices. | | +| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | +| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | +| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | +| [`axi_xbar`](src/axi_xbar.sv) | Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | +| [`axi_xbar_unmuxed`](src/axi_xbar_unmuxed.sv) | Demux side of fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | +| [`axi_xp`](src/axi_xp.sv) | AXI Crosspoint (XP) with homomorphous slave and master ports. | | +| [`axi_zero_mem`](src/axi_zero_mem.sv) | AXI-attached /dev/zero. All reads will be zero, writes are absorbed. | | + +## Synthesizable Verification Modules + +The following modules are meant to be used for verification purposes only but are synthesizable to be used in FPGA environments. + +| Name | Description | +|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| [`axi_bus_compare`](src/axi_bus_compare.sv) | Compares two buses of the same type (and in the same clock domain), returns events on mismatch. | +| [`axi_slave_compare`](src/axi_slave_compare.sv) | Compares two slave devices of the same type (and in the same clock domain), returns events on mismatch. | ### Simulation-Only Modules @@ -58,8 +86,11 @@ In addition to the modules above, which are available in synthesis and simulatio | Name | Description | |------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| [`axi_chan_compare`](src/axi_chan_compare.sv) | Non-synthesizable module comparing two AXI channels of the same type | | [`axi_chan_logger`](src/axi_test.sv) | Logs the transactions of an AXI4(+ATOPs) port to files. | | [`axi_driver`](src/axi_test.sv) | Low-level driver for AXI4(+ATOPs) that can send and receive individual beats on any channel. | +| [`axi_dumper`](src/axi_dumper.sv) | Dumps log to file to be interpreted by `axi_dumper_interpret` script for debugging purposes. | +| [`axi_file_master`](src/axi_test.sv) | AXI4 master for file-based testbenches | | [`axi_lite_driver`](src/axi_test.sv) | Low-level driver for AXI4-Lite that can send and receive individual beats on any channel. | | [`axi_lite_rand_master`](src/axi_test.sv) | AXI4-Lite master component that issues random transactions within user-defined constraints. | | [`axi_lite_rand_slave`](src/axi_test.sv) | AXI4-Lite slave component that responds to transactions with constrainable random delays and data. | @@ -72,7 +103,7 @@ In addition to the modules above, which are available in synthesis and simulatio ## Atomic Operations -AXI4+ATOPs means the full AXI4 specification plus atomic operations (ATOPs) as defined in Section E2.1 of the AMBA5 specification. This has the following implications for modules that do not implement ATOPs and systems that include such modules: +AXI4+ATOPs means the full AXI4 specification plus atomic operations (ATOPs) as defined in Section E1.1 of the [AMBA 5 specification][AMBA 5 Spec]. This has the following implications for modules that do not implement ATOPs and systems that include such modules: - Masters that do not issue ATOPs must set `aw_atop` to `'0`. - Slaves that do not support ATOPs must specify this in their interface documentation and can ignore the `aw_atop` signal. @@ -80,7 +111,9 @@ AXI4+ATOPs means the full AXI4 specification plus atomic operations (ATOPs) as d 1. slaves that do not support ATOPs are behind an [`axi_atop_filter`](src/axi_atop_filter.sv) if any master could issue an ATOP to such slaves and 2. the `aw_atop` signal is well-defined at the input of any (non-AXI4-Lite) module in this repository. -Masters and slaves that do support ATOPs must adhere to Section E2.1 of the AMBA5 specification. +Masters and slaves that do support ATOPs must adhere to Section E1.1 of the [AMBA 5 specification][AMBA 5 Spec]. In particular: +- ATOPs that have the `aw_atop[axi_pkg::ATOP_R_RESP]` bit set generate a write response (B channel) beat and at least one read response (R channel) beat. All modules for which the `aw_atop[axi_pkg::ATOP_R_RESP]` bit could be set at their master port must be able to handle both B and R beats (in any order and without requiring a simultaneous handshake) for each such ATOP request. All modules for which the `aw_atop[axi_pkg::ATOP_R_RESP]` bit could be set at their slave port must respond with the appropriate number of B and R beats for each such ATOP request. +- ATOPs must not use the same AXI ID as any other transaction that is outstanding at the same time. ## Which EDA Tools Are Supported? @@ -93,8 +126,15 @@ We aim to be compatible with a wide range of EDA tools. For this reason, we str - the workaround does not break functionality in other tools, and - the workaround does not significantly complicate code or add maintenance overhead. +In addition, we suggest to report issues with the SystemVerilog language support directly to the EDA vendor. Our code is fully open and +can / should be shared with the EDA vendor as a testcase for any language problem encountered. + All code in each release and on the default branch is tested on a recent version of at least one industry-standard RTL simulator and synthesizer. You can examine the [CI settings](./.gitlab-ci.yml) to find out which version of which tool we are running. +[AMBA 5 Spec]: https://developer.arm.com/documentation/ihi0022/hc [IEEE 1800-2012]: https://standards.ieee.org/standard/1800-2012.html +[doc.axi_id_remap]: https://pulp-platform.github.io/axi/master/module.axi_id_remap +[doc.axi_id_serialize]: https://pulp-platform.github.io/axi/master/module.axi_id_serialize +[doc.axi_iw_converter]: https://pulp-platform.github.io/axi/master/module.axi_iw_converter [doc.axi_lite_regs]: https://pulp-platform.github.io/axi/master/module.axi_lite_regs diff --git a/hw/vendor/pulp_platform_axi/VERSION b/hw/vendor/pulp_platform_axi/VERSION index 25939d35c..c02e508ca 100644 --- a/hw/vendor/pulp_platform_axi/VERSION +++ b/hw/vendor/pulp_platform_axi/VERSION @@ -1 +1 @@ -0.29.1 +0.39.4 diff --git a/hw/vendor/pulp_platform_axi/axi.core b/hw/vendor/pulp_platform_axi/axi.core index 090d7e09a..5d17f61b9 100644 --- a/hw/vendor/pulp_platform_axi/axi.core +++ b/hw/vendor/pulp_platform_axi/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.29.1 +name : pulp-platform.org::axi:0.39.4 filesets: rtl: @@ -17,18 +17,24 @@ filesets: # Level 2 - src/axi_atop_filter.sv - src/axi_burst_splitter.sv + - src/axi_bus_compare.sv - src/axi_cdc_dst.sv - src/axi_cdc_src.sv - src/axi_cut.sv - src/axi_delayer.sv - - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv + - src/axi_fifo.sv + - src/axi_id_remap.sv - src/axi_id_prepend.sv - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv + - src/axi_lite_from_mem.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -36,19 +42,74 @@ filesets: - src/axi_lite_to_axi.sv - src/axi_modify_address.sv - src/axi_mux.sv + - src/axi_rw_join.sv + - src/axi_rw_split.sv - src/axi_serializer.sv + - src/axi_slave_compare.sv + - src/axi_throttle.sv + - src/axi_to_detailed_mem.sv # Level 3 - src/axi_cdc.sv + - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv + - src/axi_from_mem.sv + - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv + - src/axi_to_mem.sv + - src/axi_zero_mem.sv # Level 4 + - src/axi_interleaved_xbar.sv + - src/axi_iw_converter.sv - src/axi_lite_xbar.sv + - src/axi_xbar_unmuxed.sv + - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv + - src/axi_chan_compare.sv + - src/axi_dumper.sv + - src/axi_sim_mem.sv + - src/axi_test.sv + # Level 5 - src/axi_xbar.sv + # Level 6 + - src/axi_xp.sv + file_type : systemVerilogSource + depend : + - ">=pulp-platform.org::common_cells:1.31.1" + benchs: + files: + - test/tb_axi_dw_pkg.sv + - test/tb_axi_xbar_pkg.sv + - test/axi_synth_bench.sv + - test/tb_axi_addr_test.sv + - test/tb_axi_atop_filter.sv + - test/tb_axi_bus_compare.sv + - test/tb_axi_cdc.sv + - test/tb_axi_delayer.sv + - test/tb_axi_dw_downsizer.sv + - test/tb_axi_dw_upsizer.sv + - test/tb_axi_fifo.sv + - test/tb_axi_isolate.sv + - test/tb_axi_iw_converter.sv + - test/tb_axi_lite_dw_converter.sv + - test/tb_axi_lite_mailbox.sv + - test/tb_axi_lite_regs.sv + - test/tb_axi_lite_to_apb.sv + - test/tb_axi_lite_to_axi.sv + - test/tb_axi_lite_xbar.sv + - test/tb_axi_modify_address.sv + - test/tb_axi_serializer.sv + - test/tb_axi_sim_mem.sv + - test/tb_axi_slave_compare.sv + - test/tb_axi_to_axi_lite.sv + - test/tb_axi_to_mem_banked.sv + - test/tb_axi_xbar.sv file_type : systemVerilogSource depend : - - ">=pulp-platform.org::common_cells:1.21.0" + - ">=pulp-platform.org::common_verification:0.2.3" generators: axi_intercon_gen: @@ -104,3 +165,29 @@ generators: targets: default: filesets : [rtl] + sim: &sim + filesets : [rtl,benchs] + description: Simulate the design + toplevel: tb_axi_delayer + sim_dw_downsizer: + <<: *sim + toplevel: tb_axi_dw_downsizer + sim_addr_test : { filesets : [rtl,benchs] , toplevel: tb_axi_addr_test } + sim_atop_filter : { filesets : [rtl,benchs] , toplevel: tb_axi_atop_filter } + sim_cdc : { filesets : [rtl,benchs] , toplevel: tb_axi_cdc } + sim_delayer : { filesets : [rtl,benchs] , toplevel: tb_axi_delayer } + sim_dw_pkg : { filesets : [rtl,benchs] , toplevel: tb_axi_dw_pkg } + sim_dw_upsizer : { filesets : [rtl,benchs] , toplevel: tb_axi_dw_upsizer } + sim_isolate : { filesets : [rtl,benchs] , toplevel: tb_axi_isolate } + sim_iw_converter : { filesets : [rtl,benchs] , toplevel: tb_axi_iw_converter } + sim_lite_mailbox : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_mailbox } + sim_lite_regs : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_regs } + sim_lite_to_apb : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_to_apb } + sim_lite_to_axi : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_to_axi } + sim_lite_xbar : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_xbar } + sim_modify_address: { filesets : [rtl,benchs] , toplevel: tb_axi_modify_address } + sim_serializer : { filesets : [rtl,benchs] , toplevel: tb_axi_serializer } + sim_sim_mem : { filesets : [rtl,benchs] , toplevel: tb_axi_sim_mem } + sim_to_axi_lite : { filesets : [rtl,benchs] , toplevel: tb_axi_to_axi_lite } + sim_xbar : { filesets : [rtl,benchs] , toplevel: tb_axi_xbar } + sim_xbar_pkg : { filesets : [rtl,benchs] , toplevel: tb_axi_xbar_pkg } diff --git a/hw/vendor/pulp_platform_axi/doc/svg/axi_id_remap_table.svg b/hw/vendor/pulp_platform_axi/doc/svg/axi_id_remap_table.svg new file mode 100644 index 000000000..8e4277cf8 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/doc/svg/axi_id_remap_table.svg @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + idx_t + 0 + 1 + 2 + ... + inp_id_t + cnt_t + + + + + + + + + + + entry_t + + table_{d,q} + + diff --git a/hw/vendor/pulp_platform_axi/include/axi/assign.svh b/hw/vendor/pulp_platform_axi/include/axi/assign.svh index 14bb1944c..739038f6c 100644 --- a/hw/vendor/pulp_platform_axi/include/axi/assign.svh +++ b/hw/vendor/pulp_platform_axi/include/axi/assign.svh @@ -11,7 +11,7 @@ // // Authors: // - Andreas Kurth -// - Wolfgang Roenninger +// - Nils Wistoff // Macros to assign AXI Interfaces and Structs @@ -538,4 +538,160 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros for assigning flattened AXI ports to req/resp AXI structs +// Flat AXI ports are required by the Vivado IP Integrator. Vivado naming convention is followed. +// +// Usage Example: +// `AXI_ASSIGN_MASTER_TO_FLAT("my_bus", my_req_struct, my_rsp_struct) +`define AXI_ASSIGN_MASTER_TO_FLAT(pat, req, rsp) \ + assign m_axi_``pat``_awvalid = req.aw_valid; \ + assign m_axi_``pat``_awid = req.aw.id; \ + assign m_axi_``pat``_awaddr = req.aw.addr; \ + assign m_axi_``pat``_awlen = req.aw.len; \ + assign m_axi_``pat``_awsize = req.aw.size; \ + assign m_axi_``pat``_awburst = req.aw.burst; \ + assign m_axi_``pat``_awlock = req.aw.lock; \ + assign m_axi_``pat``_awcache = req.aw.cache; \ + assign m_axi_``pat``_awprot = req.aw.prot; \ + assign m_axi_``pat``_awqos = req.aw.qos; \ + assign m_axi_``pat``_awregion = req.aw.region; \ + assign m_axi_``pat``_awuser = req.aw.user; \ + \ + assign m_axi_``pat``_wvalid = req.w_valid; \ + assign m_axi_``pat``_wdata = req.w.data; \ + assign m_axi_``pat``_wstrb = req.w.strb; \ + assign m_axi_``pat``_wlast = req.w.last; \ + assign m_axi_``pat``_wuser = req.w.user; \ + \ + assign m_axi_``pat``_bready = req.b_ready; \ + \ + assign m_axi_``pat``_arvalid = req.ar_valid; \ + assign m_axi_``pat``_arid = req.ar.id; \ + assign m_axi_``pat``_araddr = req.ar.addr; \ + assign m_axi_``pat``_arlen = req.ar.len; \ + assign m_axi_``pat``_arsize = req.ar.size; \ + assign m_axi_``pat``_arburst = req.ar.burst; \ + assign m_axi_``pat``_arlock = req.ar.lock; \ + assign m_axi_``pat``_arcache = req.ar.cache; \ + assign m_axi_``pat``_arprot = req.ar.prot; \ + assign m_axi_``pat``_arqos = req.ar.qos; \ + assign m_axi_``pat``_arregion = req.ar.region; \ + assign m_axi_``pat``_aruser = req.ar.user; \ + \ + assign m_axi_``pat``_rready = req.r_ready; \ + \ + assign rsp.aw_ready = m_axi_``pat``_awready; \ + assign rsp.ar_ready = m_axi_``pat``_arready; \ + assign rsp.w_ready = m_axi_``pat``_wready; \ + \ + assign rsp.b_valid = m_axi_``pat``_bvalid; \ + assign rsp.b.id = m_axi_``pat``_bid; \ + assign rsp.b.resp = m_axi_``pat``_bresp; \ + assign rsp.b.user = m_axi_``pat``_buser; \ + \ + assign rsp.r_valid = m_axi_``pat``_rvalid; \ + assign rsp.r.id = m_axi_``pat``_rid; \ + assign rsp.r.data = m_axi_``pat``_rdata; \ + assign rsp.r.resp = m_axi_``pat``_rresp; \ + assign rsp.r.last = m_axi_``pat``_rlast; \ + assign rsp.r.user = m_axi_``pat``_ruser; + +`define AXI_ASSIGN_SLAVE_TO_FLAT(pat, req, rsp) \ + assign req.aw_valid = s_axi_``pat``_awvalid; \ + assign req.aw.id = s_axi_``pat``_awid; \ + assign req.aw.addr = s_axi_``pat``_awaddr; \ + assign req.aw.len = s_axi_``pat``_awlen; \ + assign req.aw.size = s_axi_``pat``_awsize; \ + assign req.aw.burst = s_axi_``pat``_awburst; \ + assign req.aw.lock = s_axi_``pat``_awlock; \ + assign req.aw.cache = s_axi_``pat``_awcache; \ + assign req.aw.prot = s_axi_``pat``_awprot; \ + assign req.aw.qos = s_axi_``pat``_awqos; \ + assign req.aw.region = s_axi_``pat``_awregion; \ + assign req.aw.user = s_axi_``pat``_awuser; \ + \ + assign req.w_valid = s_axi_``pat``_wvalid; \ + assign req.w.data = s_axi_``pat``_wdata; \ + assign req.w.strb = s_axi_``pat``_wstrb; \ + assign req.w.last = s_axi_``pat``_wlast; \ + assign req.w.user = s_axi_``pat``_wuser; \ + \ + assign req.b_ready = s_axi_``pat``_bready; \ + \ + assign req.ar_valid = s_axi_``pat``_arvalid; \ + assign req.ar.id = s_axi_``pat``_arid; \ + assign req.ar.addr = s_axi_``pat``_araddr; \ + assign req.ar.len = s_axi_``pat``_arlen; \ + assign req.ar.size = s_axi_``pat``_arsize; \ + assign req.ar.burst = s_axi_``pat``_arburst; \ + assign req.ar.lock = s_axi_``pat``_arlock; \ + assign req.ar.cache = s_axi_``pat``_arcache; \ + assign req.ar.prot = s_axi_``pat``_arprot; \ + assign req.ar.qos = s_axi_``pat``_arqos; \ + assign req.ar.region = s_axi_``pat``_arregion; \ + assign req.ar.user = s_axi_``pat``_aruser; \ + \ + assign req.r_ready = s_axi_``pat``_rready; \ + \ + assign s_axi_``pat``_awready = rsp.aw_ready; \ + assign s_axi_``pat``_arready = rsp.ar_ready; \ + assign s_axi_``pat``_wready = rsp.w_ready; \ + \ + assign s_axi_``pat``_bvalid = rsp.b_valid; \ + assign s_axi_``pat``_bid = rsp.b.id; \ + assign s_axi_``pat``_bresp = rsp.b.resp; \ + assign s_axi_``pat``_buser = rsp.b.user; \ + \ + assign s_axi_``pat``_rvalid = rsp.r_valid; \ + assign s_axi_``pat``_rid = rsp.r.id; \ + assign s_axi_``pat``_rdata = rsp.r.data; \ + assign s_axi_``pat``_rresp = rsp.r.resp; \ + assign s_axi_``pat``_rlast = rsp.r.last; \ + assign s_axi_``pat``_ruser = rsp.r.user; +//////////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros instantiating signal highlighter from common verification +// https://github.com/pulp-platform/common_verification.git +// `AXI_HIGHLIGHT(__name, __aw_t, __w_t, __b_t, __ar_t, __r_t, __req, __resp) +`define AXI_HIGHLIGHT(__name, __aw_t, __w_t, __b_t, __ar_t, __r_t, __req, __resp) \ + signal_highlighter #( \ + .T ( __aw_t ) \ + ) i_signal_highlighter_``__name``_aw ( \ + .ready_i ( __resp.aw_ready ), \ + .valid_i ( __req.aw_valid ), \ + .data_i ( __req.aw ) \ + ); \ + signal_highlighter #( \ + .T ( __w_t ) \ + ) i_signal_highlighter_``__name``_w ( \ + .ready_i ( __resp.w_ready ), \ + .valid_i ( __req.w_valid ), \ + .data_i ( __req.w ) \ + ); \ + signal_highlighter #( \ + .T ( __b_t ) \ + ) i_signal_highlighter_``__name``_b ( \ + .ready_i ( __req.b_ready ), \ + .valid_i ( __resp.b_valid ), \ + .data_i ( __resp.b ) \ + ); \ + signal_highlighter #( \ + .T ( __ar_t ) \ + ) i_signal_highlighter_``__name``_ar ( \ + .ready_i ( __resp.ar_ready ), \ + .valid_i ( __req.ar_valid ), \ + .data_i ( __req.ar ) \ + ); \ + signal_highlighter #( \ + .T ( __r_t ) \ + ) i_signal_highlighter_``__name``_r ( \ + .ready_i ( __req.r_ready ), \ + .valid_i ( __resp.r_valid ), \ + .data_i ( __resp.r ) \ + ); +//////////////////////////////////////////////////////////////////////////////////////////////////// + `endif diff --git a/hw/vendor/pulp_platform_axi/include/axi/port.svh b/hw/vendor/pulp_platform_axi/include/axi/port.svh new file mode 100644 index 000000000..c3d7a4a7f --- /dev/null +++ b/hw/vendor/pulp_platform_axi/include/axi/port.svh @@ -0,0 +1,120 @@ +// Copyright (c) 2014-2023 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +// Macros to add AXI ports + +`ifndef AXI_PORT_SVH_ +`define AXI_PORT_SVH_ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros creating flat AXI ports +// `AXI_M_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) +`define AXI_M_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) \ + output logic m_axi_``__name``_awvalid, \ + output __id_t m_axi_``__name``_awid, \ + output __addr_t m_axi_``__name``_awaddr, \ + output axi_pkg::len_t m_axi_``__name``_awlen, \ + output axi_pkg::size_t m_axi_``__name``_awsize, \ + output axi_pkg::burst_t m_axi_``__name``_awburst, \ + output logic m_axi_``__name``_awlock, \ + output axi_pkg::cache_t m_axi_``__name``_awcache, \ + output axi_pkg::prot_t m_axi_``__name``_awprot, \ + output axi_pkg::qos_t m_axi_``__name``_awqos, \ + output axi_pkg::region_t m_axi_``__name``_awregion, \ + output __aw_user_t m_axi_``__name``_awuser, \ + output logic m_axi_``__name``_wvalid, \ + output __data_t m_axi_``__name``_wdata, \ + output __strb_t m_axi_``__name``_wstrb, \ + output logic m_axi_``__name``_wlast, \ + output __w_user_t m_axi_``__name``_wuser, \ + output logic m_axi_``__name``_bready, \ + output logic m_axi_``__name``_arvalid, \ + output __id_t m_axi_``__name``_arid, \ + output __addr_t m_axi_``__name``_araddr, \ + output axi_pkg::len_t m_axi_``__name``_arlen, \ + output axi_pkg::size_t m_axi_``__name``_arsize, \ + output axi_pkg::burst_t m_axi_``__name``_arburst, \ + output logic m_axi_``__name``_arlock, \ + output axi_pkg::cache_t m_axi_``__name``_arcache, \ + output axi_pkg::prot_t m_axi_``__name``_arprot, \ + output axi_pkg::qos_t m_axi_``__name``_arqos, \ + output axi_pkg::region_t m_axi_``__name``_arregion, \ + output __ar_user_t m_axi_``__name``_aruser, \ + output logic m_axi_``__name``_rready, \ + input logic m_axi_``__name``_awready, \ + input logic m_axi_``__name``_arready, \ + input logic m_axi_``__name``_wready, \ + input logic m_axi_``__name``_bvalid, \ + input __id_t m_axi_``__name``_bid, \ + input axi_pkg::resp_t m_axi_``__name``_bresp, \ + input __b_user_t m_axi_``__name``_buser, \ + input logic m_axi_``__name``_rvalid, \ + input __id_t m_axi_``__name``_rid, \ + input __data_t m_axi_``__name``_rdata, \ + input axi_pkg::resp_t m_axi_``__name``_rresp, \ + input logic m_axi_``__name``_rlast, \ + input __r_user_t m_axi_``__name``_ruser, \ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros creating flat AXI ports +// `AXI_S_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) +`define AXI_S_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) \ + input logic s_axi_``__name``_awvalid, \ + input __id_t s_axi_``__name``_awid, \ + input __addr_t s_axi_``__name``_awaddr, \ + input axi_pkg::len_t s_axi_``__name``_awlen, \ + input axi_pkg::size_t s_axi_``__name``_awsize, \ + input axi_pkg::burst_t s_axi_``__name``_awburst, \ + input logic s_axi_``__name``_awlock, \ + input axi_pkg::cache_t s_axi_``__name``_awcache, \ + input axi_pkg::prot_t s_axi_``__name``_awprot, \ + input axi_pkg::qos_t s_axi_``__name``_awqos, \ + input axi_pkg::region_t s_axi_``__name``_awregion, \ + input __aw_user_t s_axi_``__name``_awuser, \ + input logic s_axi_``__name``_wvalid, \ + input __data_t s_axi_``__name``_wdata, \ + input __strb_t s_axi_``__name``_wstrb, \ + input logic s_axi_``__name``_wlast, \ + input __w_user_t s_axi_``__name``_wuser, \ + input logic s_axi_``__name``_bready, \ + input logic s_axi_``__name``_arvalid, \ + input __id_t s_axi_``__name``_arid, \ + input __addr_t s_axi_``__name``_araddr, \ + input axi_pkg::len_t s_axi_``__name``_arlen, \ + input axi_pkg::size_t s_axi_``__name``_arsize, \ + input axi_pkg::burst_t s_axi_``__name``_arburst, \ + input logic s_axi_``__name``_arlock, \ + input axi_pkg::cache_t s_axi_``__name``_arcache, \ + input axi_pkg::prot_t s_axi_``__name``_arprot, \ + input axi_pkg::qos_t s_axi_``__name``_arqos, \ + input axi_pkg::region_t s_axi_``__name``_arregion, \ + input __ar_user_t s_axi_``__name``_aruser, \ + input logic s_axi_``__name``_rready, \ + output logic s_axi_``__name``_awready, \ + output logic s_axi_``__name``_arready, \ + output logic s_axi_``__name``_wready, \ + output logic s_axi_``__name``_bvalid, \ + output __id_t s_axi_``__name``_bid, \ + output axi_pkg::resp_t s_axi_``__name``_bresp, \ + output __b_user_t s_axi_``__name``_buser, \ + output logic s_axi_``__name``_rvalid, \ + output __id_t s_axi_``__name``_rid, \ + output __data_t s_axi_``__name``_rdata, \ + output axi_pkg::resp_t s_axi_``__name``_rresp, \ + output logic s_axi_``__name``_rlast, \ + output __r_user_t s_axi_``__name``_ruser, \ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +`endif diff --git a/hw/vendor/pulp_platform_axi/include/axi/typedef.svh b/hw/vendor/pulp_platform_axi/include/axi/typedef.svh index a2a860e50..648c3fed7 100644 --- a/hw/vendor/pulp_platform_axi/include/axi/typedef.svh +++ b/hw/vendor/pulp_platform_axi/include/axi/typedef.svh @@ -11,6 +11,7 @@ // // Authors: // - Andreas Kurth +// - Thomas Benz // - Florian Zaruba // - Wolfgang Roenninger @@ -104,6 +105,28 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// All AXI4+ATOP Channels and Request/Response Structs in One Macro - Custom Type Name Version +// +// This can be used whenever the user is not interested in "precise" control of the naming of the +// individual channels. +// +// Usage Example: +// `AXI_TYPEDEF_ALL_CT(axi, axi_req_t, axi_rsp_t, addr_t, id_t, data_t, strb_t, user_t) +// +// This defines `axi_req_t` and `axi_rsp_t` request/response structs as well as `axi_aw_chan_t`, +// `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs. +`define AXI_TYPEDEF_ALL_CT(__name, __req, __rsp, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t) \ + `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t) \ + `AXI_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t) \ + `AXI_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t) \ + `AXI_TYPEDEF_REQ_T(__req, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ + `AXI_TYPEDEF_RESP_T(__rsp, __name``_b_chan_t, __name``_r_chan_t) +//////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////// // All AXI4+ATOP Channels and Request/Response Structs in One Macro // @@ -115,14 +138,8 @@ // // This defines `axi_req_t` and `axi_resp_t` request/response structs as well as `axi_aw_chan_t`, // `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs. -`define AXI_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ - `AXI_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t) \ - `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t) \ - `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t) \ - `AXI_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t) \ - `AXI_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t) \ - `AXI_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t) +`define AXI_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_ALL_CT(__name, __name``_req_t, __name``_resp_t, __addr_t, __id_t, __data_t, __strb_t, __user_t) //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -185,6 +202,29 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// All AXI4-Lite Channels and Request/Response Structs in One Macro - Custom Type Name Version +// +// This can be used whenever the user is not interested in "precise" control of the naming of the +// individual channels. +// +// Usage Example: +// `AXI_LITE_TYPEDEF_ALL_CT(axi_lite, axi_lite_req_t, axi_lite_rsp_t, addr_t, data_t, strb_t) +// +// This defines `axi_lite_req_t` and `axi_lite_resp_t` request/response structs as well as +// `axi_lite_aw_chan_t`, `axi_lite_w_chan_t`, `axi_lite_b_chan_t`, `axi_lite_ar_chan_t`, and +// `axi_lite_r_chan_t` channel structs. +`define AXI_LITE_TYPEDEF_ALL_CT(__name, __req, __rsp, __addr_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t) \ + `AXI_LITE_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_B_CHAN_T(__name``_b_chan_t) \ + `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ + `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ + `AXI_LITE_TYPEDEF_REQ_T(__req, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ + `AXI_LITE_TYPEDEF_RESP_T(__rsp, __name``_b_chan_t, __name``_r_chan_t) +//////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////// // All AXI4-Lite Channels and Request/Response Structs in One Macro // @@ -197,14 +237,8 @@ // This defines `axi_lite_req_t` and `axi_lite_resp_t` request/response structs as well as // `axi_lite_aw_chan_t`, `axi_lite_w_chan_t`, `axi_lite_b_chan_t`, `axi_lite_ar_chan_t`, and // `axi_lite_r_chan_t` channel structs. -`define AXI_LITE_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ - `AXI_LITE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t) \ - `AXI_LITE_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t) \ - `AXI_LITE_TYPEDEF_B_CHAN_T(__name``_b_chan_t) \ - `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ - `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ - `AXI_LITE_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_LITE_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t) +`define AXI_LITE_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_ALL_CT(__name, __name``_req_t, __name``_resp_t, __addr_t, __data_t, __strb_t) //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/hw/vendor/pulp_platform_axi/ips_list.yml b/hw/vendor/pulp_platform_axi/ips_list.yml index c8afeda72..7d797dea5 100644 --- a/hw/vendor/pulp_platform_axi/ips_list.yml +++ b/hw/vendor/pulp_platform_axi/ips_list.yml @@ -1,7 +1,11 @@ common_cells: - commit: v1.21.0 + commit: v1.31.1 group: pulp-platform common_verification: - commit: v0.2.0 + commit: v0.2.3 + group: pulp-platform + +tech_cells_generic: + commit: v0.2.2 group: pulp-platform diff --git a/hw/vendor/pulp_platform_axi/scripts/axi_intercon_gen.py b/hw/vendor/pulp_platform_axi/scripts/axi_intercon_gen.py deleted file mode 100644 index af5b3a209..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/axi_intercon_gen.py +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/env python3 -import math -import sys -from collections import OrderedDict, defaultdict -import yaml - -from verilogwriter import Signal, Wire, Instance, ModulePort, Port, VerilogWriter - -if sys.version[0] == '2': - - math.log2 = lambda x : math.log(x, 2) - -class Widths: - addr = 0 - user = 0 - data = 0 - max_id = 0 - -def axi_signals(w, id_width): - signals = [ - ("aw", "id" , False, id_width), - ("aw", "addr" , False, w.addr ), - ("aw", "len" , False, 8 ), - ("aw", "size" , False, 3 ), - ("aw", "burst" , False, 2 ), - ("aw", "lock" , False, 0 ), - ("aw", "cache" , False, 4 ), - ("aw", "prot" , False, 3 ), - ("aw", "region", False, 4), - ("aw", "user" , False, w.user), - ("aw", "qos" , False, 4), - ("aw", "atop" , False, 6), - ("aw", "valid" , False, 0), - ("aw", "ready" , True , 0), - - ("ar", "id" , False, id_width), - ("ar", "addr" , False, w.addr), - ("ar", "len" , False, 8), - ("ar", "size" , False, 3), - ("ar", "burst" , False, 2), - ("ar", "lock" , False, 0), - ("ar", "cache" , False, 4), - ("ar", "prot" , False, 3), - ("ar", "region", False, 4), - ("ar", "user" , False, w.user), - ("ar", "qos" , False, 4), - ("ar", "valid" , False, 0), - ("ar", "ready" , True , 0), - - ("w", "data" , False, w.data), - ("w", "strb" , False, w.data//8), - ("w", "last" , False, 0), - ("w", "user" , False, w.user), - ("w", "valid", False, 0), - ("w", "ready", True , 0), - - ("b", "id" , True , id_width), - ("b", "resp" , True , 2), - ("b", "valid", True , 0), - ("b", "user" , True, w.user), - ("b", "ready", False, 0), - - ("r", "id" , True , id_width), - ("r", "data" , True , w.data), - ("r", "resp" , True , 2), - ("r", "last" , True , 0), - ("r", "user" , True, w.user), - ("r", "valid", True , 0), - ("r", "ready", False, 0), - ] - return signals - -def module_ports(w, intf, id_width, is_input): - ports = [] - for (ch, name, _dir, width) in axi_signals(w, id_width): - if name == 'user' and not w.user: - continue - if name == 'atop' and not w.atop: - continue - if ch in ['aw', 'w', 'b'] and intf.read_only: - continue - prefix = 'o' if is_input == _dir else 'i' - ports.append(ModulePort("{}_{}_{}{}".format(prefix, intf.name, ch, name), - 'output' if is_input == _dir else 'input', - width)) - return ports - -def assigns(w, max_idw, masters, slaves): - raw = '\n' - i = 0 - for m in masters: - raw += " //Master {}\n".format(m.name) - for (ch, name, _dir, width) in axi_signals(w, m.idw): - if name in ['valid','ready']: - _name = ch+'_'+name - else: - _name = ch+'.'+name - if name == 'user' and not w.user: - continue - if _dir: - if m.read_only and ((ch == 'aw') or (ch == 'w') or (ch == 'b')): - continue - - src = "masters_resp[{}].{}".format(i, _name) - if ch in ['b', 'r'] and name == 'id' and m.idw < max_idw: - src = src+'[{}:0]'.format(m.idw-1) - raw += " assign o_{}_{}{} = {};\n".format(m.name, ch, name, src) - else: - src = "i_{}_{}{}".format(m.name, ch, name) - if name == 'atop' and not w.atop: - src = "6'd0" - if ch in ['ar', 'aw'] and name == 'id' and m.idw < max_idw: - src = "{"+ str(max_idw-m.idw)+"'d0,"+src+"}" - if m.read_only and (ch in ['aw' , 'w', 'b']): - if ch == 'aw' and name == 'id': - _w = max_idw - else: - _w = max(1,width) - src = "{}'d0".format(_w) - raw += " assign masters_req[{}].{} = {};\n".format(i, _name, src) - raw += "\n" - i += 1 - - i = 0 - for m in slaves: - raw += " //Slave {}\n".format(m.name) - for (ch, name, _dir, width) in axi_signals(w, max_idw): - if name == 'user' and not w.user: - continue - if name == 'atop' and not w.atop: - continue - if _dir: - if name in ['valid','ready']: - _name = ch+'_'+name - else: - _name = ch+'.'+name - raw += " assign slaves_resp[{}].{} = i_{}_{}{};\n".format(i, _name, m.name, ch, name) - else: - if name in ['valid','ready']: - _name = ch+'_'+name - else: - _name = ch+'.'+name - raw += " assign o_{}_{}{} = slaves_req[{}].{};\n".format(m.name, ch, name, i, _name) - i += 1 - raw += "\n" - - raw += "\n" - return raw - -def instance_ports(w, id_width, masters, slaves): - ports = [Port('clk_i' , 'clk_i'), - Port('rst_ni', 'rst_ni'), - Port('test_i', "1'b0"), - Port('slv_ports_req_i' , 'masters_req'), - Port('slv_ports_resp_o', 'masters_resp'), - Port('mst_ports_req_o' , 'slaves_req'), - Port('mst_ports_resp_i', 'slaves_resp'), - Port('addr_map_i' , 'AddrMap'), - Port('en_default_mst_port_i', "{}'d0".format(len(masters))), - Port('default_mst_port_i', "'0"), - ] - return ports - -def template_ports(w, intf, id_width, is_input): - ports = [] - for (ch, name, _dir, width) in axi_signals(w, id_width): - if name == 'user' and not w.user: - continue - if name == 'atop' and not w.atop: - continue - if intf.read_only and ch in ['aw', 'w', 'b']: - continue - port_name = "{}_{}{}".format(intf.name, ch, name) - prefix = 'o' if is_input == _dir else 'i' - ports.append(Port("{}_{}".format(prefix, port_name), port_name)) - return ports - -def template_wires(w, intf, id_width): - wires = [] - for (ch, name, _dir, width) in axi_signals(w, id_width): - if name == 'user' and not w.user: - continue - if name == 'atop' and not w.atop: - continue - if intf.read_only and ch in ['aw', 'w', 'b']: - continue - wires.append(Wire("{}_{}{}".format(intf.name, ch, name), width)) - return wires - -class Master: - def __init__(self, name, d=None): - self.name = name - self.slaves = [] - self.idw = 1 - self.read_only = False - if d: - self.load_dict(d) - - def load_dict(self, d): - for key, value in d.items(): - if key == 'slaves': - # Handled in file loading, ignore here - continue - if key == 'id_width': - self.idw = value - elif key == 'read_only': - self.read_only = value - else: - print(key) - raise UnknownPropertyError( - "Unknown property '%s' in master section '%s'" % ( - key, self.name)) - -class Slave: - def __init__(self, name, d=None): - self.name = name - self.masters = [] - self.offset = 0 - self.size = 0 - self.mask = 0 - self.read_only = False - if d: - self.load_dict(d) - - def load_dict(self, d): - for key, value in d.items(): - if key == 'offset': - self.offset = value - elif key == 'size': - self.size = value - self.mask = ~(self.size-1) & 0xffffffff - elif key == 'read_only': - self.read_only = value - else: - raise UnknownPropertyError( - "Unknown property '%s' in slave section '%s'" % ( - key, self.name)) - -class Parameter: - def __init__(self, name, value): - self.name = name - self.value = value - - - -class AxiIntercon: - def __init__(self, name, config_file): - self.verilog_writer = VerilogWriter(name) - self.template_writer = VerilogWriter(name) - self.name = name - d = OrderedDict() - self.slaves = [] - self.masters = [] - import yaml - - def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict): - class OrderedLoader(Loader): - pass - def construct_mapping(loader, node): - loader.flatten_mapping(node) - return object_pairs_hook(loader.construct_pairs(node)) - OrderedLoader.add_constructor( - yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, - construct_mapping) - return yaml.load(stream, OrderedLoader) - data = ordered_load(open(config_file)) - - config = data['parameters'] - - self.vlnv = data['vlnv'] - - for k,v in config['masters'].items(): - print("Found master " + k) - self.masters.append(Master(k,v)) - for k,v in config['slaves'].items(): - print("Found slave " + k) - self.slaves.append(Slave(k,v)) - - self.output_file = config.get('output_file', 'axi_intercon.v') - self.atop = config.get('atop', False) - - def _dump(self): - print("*Masters*") - for master in self.masters.values(): - print(master.name) - for slave in master.slaves: - print(' ' + slave.name) - - print("*Slaves*") - for slave in self.slaves.values(): - print(slave.name) - for master in slave.masters: - print(' ' + master.name) - - def write(self): - w = Widths() - w.addr = 32 - w.data = 64 - w.user = 0 - w.atop = self.atop - - max_idw = max([m.idw for m in self.masters]) - max_sidw = max_idw + int(math.ceil(math.log2(len(self.masters)))) - file = self.output_file - - _template_ports = [Port('clk_i' , 'clk'), - Port('rst_ni', 'rst_n')] - template_parameters = [] - - #Module header - self.verilog_writer.add(ModulePort('clk_i' , 'input')) - self.verilog_writer.add(ModulePort('rst_ni', 'input')) - for master in self.masters: - for port in module_ports(w, master, master.idw, True): - self.verilog_writer.add(port) - for wire in template_wires(w, master, master.idw): - self.template_writer.add(wire) - _template_ports += template_ports(w, master, master.idw, True) - - for slave in self.slaves: - for port in module_ports(w, slave, max_sidw, False): - self.verilog_writer.add(port) - for wire in template_wires(w, slave, max_sidw): - self.template_writer.add(wire) - _template_ports += template_ports(w, slave, max_sidw, False) - - raw = "" - nm = len(self.masters) - ns = len(self.slaves) - - raw += """ - localparam int unsigned NoMasters = 32'd{}; // How many Axi Masters there are - localparam int unsigned NoSlaves = 32'd{}; // How many Axi Slaves there are - - // axi configuration - localparam int unsigned AxiIdWidthMasters = 32'd{}; - localparam int unsigned AxiIdUsed = 32'd{}; // Has to be <= AxiIdWidthMasters - localparam int unsigned AxiIdWidthSlaves = AxiIdWidthMasters + $clog2(NoMasters); - localparam int unsigned AxiAddrWidth = 32'd32; // Axi Address Width - localparam int unsigned AxiDataWidth = 32'd64; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; - localparam int unsigned AxiUserWidth = 1; -""".format(nm, ns, max_idw, max_idw) - raw += " localparam axi_pkg::xbar_cfg_t xbar_cfg = '{\n" - raw += """ - NoSlvPorts: NoMasters, - NoMstPorts: NoSlaves, - MaxMstTrans: 10, - MaxSlvTrans: 6, - FallThrough: 1'b0, - LatencyMode: axi_pkg::CUT_ALL_AX, - AxiIdWidthSlvPorts: AxiIdWidthMasters, - AxiIdUsedSlvPorts: AxiIdUsed, - UniqueIds: 1'b0, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: NoSlaves -""" - raw += " };\n" - raw += """ - typedef logic [AxiIdWidthMasters-1:0] id_mst_t; - typedef logic [AxiIdWidthSlaves-1:0] id_slv_t; - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - typedef logic [AxiUserWidth-1:0] user_t; - - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_mst_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_slv_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_mst_t, id_mst_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_slv_t, id_slv_t, user_t) - - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_mst_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_slv_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_mst_t, data_t, id_mst_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_slv_t, data_t, id_slv_t, user_t) - - `AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_mst_t, w_chan_t, ar_chan_mst_t) - `AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_mst_t, r_chan_mst_t) - `AXI_TYPEDEF_REQ_T(mst_req_t, aw_chan_slv_t, w_chan_t, ar_chan_slv_t) - `AXI_TYPEDEF_RESP_T(mst_resp_t, b_chan_slv_t, r_chan_slv_t) - -""" - - raw += " localparam rule_t [{}:0] AddrMap = '".format(ns-1) - raw += "{\n" - i = 0 - rules = [] - for slave in self.slaves: - rule = " '{" - rule += "idx: 32'd{}, start_addr: 32'h{:08x}, end_addr: 32'h{:08x}".format(i, slave.offset, slave.offset+slave.size) - rule += "}" - i += 1 - rules.append(rule) - raw += ',\n'.join(rules) - raw += "};\n" - - raw += " slv_req_t [{}:0] masters_req;\n".format(nm-1) - raw += " slv_resp_t [{}:0] masters_resp;\n".format(nm-1) - - raw += " mst_req_t [{}:0] slaves_req;\n".format(ns-1) - raw += " mst_resp_t [{}:0] slaves_resp;\n".format(ns-1) - - ns = len(self.slaves) - - raw += assigns(w, max_idw, self.masters, self.slaves) - - self.verilog_writer.raw = raw - parameters = [ - Parameter('Cfg' , 'xbar_cfg' ), - Parameter('ATOPs' , "1'b"+str(int(self.atop))), - Parameter('slv_aw_chan_t', 'aw_chan_mst_t'), - Parameter('mst_aw_chan_t', 'aw_chan_slv_t'), - Parameter('w_chan_t' , 'w_chan_t' ), - Parameter('slv_b_chan_t' , 'b_chan_mst_t' ), - Parameter('mst_b_chan_t' , 'b_chan_slv_t' ), - Parameter('slv_ar_chan_t', 'ar_chan_mst_t'), - Parameter('mst_ar_chan_t', 'ar_chan_slv_t'), - Parameter('slv_r_chan_t' , 'r_chan_mst_t' ), - Parameter('mst_r_chan_t' , 'r_chan_slv_t' ), - Parameter('slv_req_t' , 'slv_req_t' ), - Parameter('slv_resp_t' , 'slv_resp_t' ), - Parameter('mst_req_t' , 'mst_req_t' ), - Parameter('mst_resp_t' , 'mst_resp_t' ), - Parameter('rule_t' , 'rule_t' ), - ] - ports = instance_ports(w, max_idw, self.masters, self.slaves) - - self.verilog_writer.add(Instance('axi_xbar', - 'axi_xbar', - parameters, - ports)) - - self.template_writer.add(Instance(self.name, - self.name, - template_parameters, - _template_ports)) - - self.verilog_writer.write(file) - self.template_writer.write(file+'h') - - core_file = self.vlnv.split(':')[2]+'.core' - vlnv = self.vlnv - with open(core_file, 'w') as f: - f.write('CAPI=2:\n') - files = [{file : {'file_type' : 'systemVerilogSource'}}, - {file+'h' : {'is_include_file' : True, - 'file_type' : 'verilogSource'}} - ] - coredata = {'name' : vlnv, - 'targets' : {'default' : {}}, - } - - coredata['filesets'] = {'rtl' : {'files' : files}} - coredata['targets']['default']['filesets'] = ['rtl'] - - f.write(yaml.dump(coredata)) - -if __name__ == "__main__": - name = "axi_intercon" - g = AxiIntercon(name, sys.argv[1]) - print("="*80) - g.write() diff --git a/hw/vendor/pulp_platform_axi/scripts/compile_vsim.sh b/hw/vendor/pulp_platform_axi/scripts/compile_vsim.sh deleted file mode 100755 index 9ed8b84d1..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/compile_vsim.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright (c) 2014-2018 ETH Zurich, University of Bologna -# -# Copyright and related rights are licensed under the Solderpad Hardware -# License, Version 0.51 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -# or agreed to in writing, software, hardware and materials distributed under -# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# -# Authors: -# - Andreas Kurth -# - Fabian Schuiki - -set -e - -[ ! -z "$VSIM" ] || VSIM=vsim - -bender script vsim -t test \ - --vlog-arg="-svinputport=compat" \ - --vlog-arg="-override_timescale 1ns/1ps" \ - --vlog-arg="-suppress 2583" \ - > compile.tcl -echo 'return 0' >> compile.tcl - -# Add `-lint -pendanticerrors` flags only for the files in this repository. -# Patching the compile script in this way is quite ugly, maybe there should be a Bender command to -# add arguments just for certain targets. -for x in axi_pkg; do - # Adapted from https://unix.stackexchange.com/a/200610. - POSIXLY_CORRECT=1 awk -v N=6 " - BEGIN{N--} - NR > N { - if (/.*src\/$x\.sv/) - print \" -lint -pedanticerrors \\\\\" - print l[NR % N] - } - {l[NR % N] = \$0} - END{ - for (i = NR > N ? NR - N + 1 : 1; i <= NR; i++) print l[i % N] - }" < compile.tcl > compile.patched.tcl - mv compile{.patched,}.tcl -done - -$VSIM -c -do 'exit -code [source compile.tcl]' diff --git a/hw/vendor/pulp_platform_axi/scripts/do_release b/hw/vendor/pulp_platform_axi/scripts/do_release deleted file mode 100755 index b2052010a..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/do_release +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2020 ETH Zurich, University of Bologna -# SPDX-License-Identifier: Apache-2.0 -# -# See `usage()` for description, or execute with the `--help` flag. -# -# Authors: -# - Andreas Kurth - -set -euo pipefail -readonly THIS_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" -readonly REPO_ROOT="$(readlink -f "$THIS_DIR/..")" - -### Settings ####################################################################################### -# Changelog -readonly CHANGELOG_FILE="$REPO_ROOT/CHANGELOG.md" -# FuseSoC -readonly FUSESOC_FILE="$REPO_ROOT/axi.core" -readonly FUSESOC_IP_VLN='pulp-platform.org::axi' -# Git -readonly GIT_MAIN_BRANCH='master' -readonly GIT_REMOTE='origin' -# Version file -readonly VERSION_FILE="$REPO_ROOT/VERSION" -#################################################################################################### - -### Generic helper functions ####################################################################### -git_cur_ver_tag() { - git describe --match 'v*' --abbrev=0 -} -semver_major() { - echo "$1" | cut -d. -f1 -} -semver_minor() { - echo "$1" | cut -d. -f2 -} -semver_noprerel() { - echo "$1" | cut -d- -f1 -} -semver_patch() { - echo "$1" | cut -d. -f3 -} -git_cur_semver_noprerel() { - local -r GIT_CUR_VER_TAG="$(git_cur_ver_tag)" - semver_noprerel "${GIT_CUR_VER_TAG:1}" -} -stderr() { - echo "$@" >&2 -} -confirm() { - read -n 1 -p "$@ [yN] " yn - case "$yn" in - [Yy]) stderr "";; - *) exit 1;; - esac -} -#################################################################################################### - -usage() { - stderr "Do a release: update version numbers in files, update Changelog, and publish on GitHub." - stderr -e "\nUsage:" - stderr -e "\t$(basename $0) [flags] " - stderr -e "\nArguments:" - stderr -e "\t\tType of release to create" - stderr -e "\nFlags:" - stderr -e "\t --dev\tIncrement development version but do not create a release" - stderr -e "\t\t\tand do not push anything." - stderr -e "\t-h, --help\tPrint usage information and exit." -} - -# Parse flags. -DEV_ONLY=false -PARAMS="" -while (( "$#" )); do - case "$1" in - --dev) - DEV_ONLY=true - shift;; - -h|--help) - usage - exit 0;; - -*|--*) # unsupported flags - stderr "Error: Unsupported flag '$1'" - exit 1;; - *) # preserve positional arguments - PARAMS="$PARAMS $1" - shift;; - esac -done -eval set -- "$PARAMS" -readonly DEV_ONLY - -# Parse positional arguments. -if test "$#" -ne 1; then - usage - exit 1 -fi -case "$1" in - major|minor|patch) - readonly REL_TYPE="$1";; - *) - usage - exit 1 -esac - -### Implementation functions ####################################################################### -# Update FuseSoC core description. -fusesoc() { - sed -i -e "s/name\s*:\s*$FUSESOC_IP_VLN.*/name : $FUSESOC_IP_VLN:$1/" "$FUSESOC_FILE" - git add "$FUSESOC_FILE" -} - -# Update `VERSION` file. -version_file() { - echo "$1" > "$VERSION_FILE" - git add "$VERSION_FILE" -} - -# Calculate the next SemVer. -# First argument: current SemVer (without pre-release suffixes). -# Second argument: type of increment (major|minor|patch). -semver_next() { - local -r CUR_MAJOR=$(semver_major $1) - local -r CUR_MINOR=$(semver_minor $1) - local -r CUR_PATCH=$(semver_patch $1) - case $2 in - major) - local -r NEW_MAJOR=$(($CUR_MAJOR + 1)) - local -r NEW_MINOR=0 - local -r NEW_PATCH=0 - ;; - minor) - local -r NEW_MAJOR=$CUR_MAJOR - local -r NEW_MINOR=$(($CUR_MINOR + 1)) - local -r NEW_PATCH=0 - ;; - patch) - local -r NEW_MAJOR=$CUR_MAJOR - local -r NEW_MINOR=$CUR_MINOR - local -r NEW_PATCH=$(($CUR_PATCH + 1)) - ;; - *) - stderr "Fatal: unreachable code reached!" - exit 1 - ;; - esac - echo "$NEW_MAJOR.$NEW_MINOR.$NEW_PATCH" -} - -# Increment development version -incr_dev_ver() { - local -r DEV_VER="$(semver_next "$(git_cur_semver_noprerel)" "$1")-dev" - fusesoc "$DEV_VER" - version_file "$DEV_VER" - git commit -m "Increment development version towards next $1 release" - stderr - git show - stderr 'Updated files to increment development version and created commit shown above.' - confirm 'Is this commit correct?' -} -#################################################################################################### - -# Make sure there are no staged changes. -if ! git diff --staged --quiet; then - stderr "Error: You have staged changes." - stderr "Commit them before running this script." - exit 1 -fi - -# Make sure the files we are about to modify contain no uncommitted changes. -ensure_clean() { - if ! git diff --quiet -- "$1"; then - stderr "Error: '$(realpath --relative-to="$REPO_ROOT" "$1")' contains uncommitted changes!" - exit 1 - fi -} -for f in "$CHANGELOG_FILE" "$FUSESOC_FILE" "$VERSION_FILE"; do - ensure_clean "$f" -done - -if $DEV_ONLY; then - incr_dev_ver $REL_TYPE - # If we only have to increment the development version, we are done. - exit 0 -fi - -# Calculate the next version. -readonly NEW_VER="$(semver_next "$(git_cur_semver_noprerel)" "$REL_TYPE")" -readonly NEW_GIT_VER_TAG="v$NEW_VER" - -# Create release branch. -readonly NEW_BRANCH="release-$NEW_VER" -git checkout -b "$NEW_BRANCH" - -# Update Changelog. -# First, remove unused subsections from `Unreleased` section. -gawk -i inplace ' -BEGIN { unreleased=0; subhdr=""; subhdrprinted=1; } -{ - # Determine if we are in the "Unreleased" section. - if (unreleased) { - if ($0 ~ /^##\s+.*$/) { - unreleased=0; - } - } else if ($0 ~ /^##\s+Unreleased$/) { - unreleased=1; - } - if (unreleased) { - # If we are in the "Unreleased" section, process subheaders. - if ($0 ~ /^###\s+.*$/) { - # Current line is a subheader - subhdr=$0; - subhdrprinted=0; - } else { - # Current line is not a subheader. - if (subhdrprinted) { - # Print current line if the subheader was already printed. - print $0; - } else if (!($0 ~ /^\s*$/)) { - # Otherwise, if current line is not empty, print the subheader and the current line. - print subhdr; - subhdrprinted=1; - print $0; - } - } - } else { - # If we are outside the "Unreleased" section, print each line as-is. - print $0; - } - prev=$0; -}' "$CHANGELOG_FILE" -# Second, extract release notes to temporary file. -readonly REL_NOTES_FILE_2="$(mktemp)" -gawk ' -BEGIN { unreleased=0; } -{ - # Determine if we are in the "Unreleased" section. - if (unreleased) { - if ($0 ~ /^##\s+.*$/) { - nextfile; - } - } else if ($0 ~ /^##\s+Unreleased$/) { - unreleased=1; - next; - } - if (unreleased) { - print $0; - } -}' "$CHANGELOG_FILE" > "$REL_NOTES_FILE_2" -readonly REL_NOTES_FILE="$(mktemp)" -# Remove empty lines from beginning and end of release notes. -# Source: https://unix.stackexchange.com/a/552198 -awk 'NF {p=1} p' <<< "$(< $REL_NOTES_FILE_2)" > "$REL_NOTES_FILE" -rm "$REL_NOTES_FILE_2" -# Third, make sure there are (still) two empty lines above every section header. -gawk -i inplace '{ - if ($0 ~ /^##\s+.*$/) { - if (!(prev ~ /^\s*$/)) { - print ""; - print ""; - } else if (!(pprev ~ /^\s*$/)) { - print ""; - } - } - print $0; - pprev=prev; - prev=$0; -}' "$CHANGELOG_FILE" -# Fourth, rename 'Unreleased' to release version and UTC date. -sed -i -e "s/Unreleased/$NEW_VER - $(date -u --iso-8601=date)/" "$CHANGELOG_FILE" -# Finally, stage Changelog modifications. -git add "$CHANGELOG_FILE" - -# Update other files for release. -fusesoc $NEW_VER -version_file $NEW_VER - -# Create release commit. -git commit -m "Release v$NEW_VER" - -# Let user review release commit, then ask for confirmation to continue. -git show -stderr 'Updated files and created release commit; it is shown above.' -confirm 'Is the release commit correct?' - -# Create Git tag for release and push tag. -git tag -a "$NEW_GIT_VER_TAG" -m "Release v$NEW_VER" -stderr "Git-tagged release commit as '$NEW_GIT_VER_TAG'." - -# Create commit to continue development. -sed -i -e '7a\ -## Unreleased\ -\ -### Added\ -\ -### Changed\ -\ -### Fixed\ -\ -' "$CHANGELOG_FILE" -git add "$CHANGELOG_FILE" -incr_dev_ver 'patch' - -# Push branch with release and post-release commit and let user review it. -git push -u "$GIT_REMOTE" "$NEW_BRANCH" -stderr -stderr "Up to this point, all commits have been created on the '$NEW_BRANCH' branch," -stderr "and the release tag only exists on your local machine." -stderr "Now review the '$NEW_BRANCH' branch." -stderr -stderr 'Then confirm that I shall continue to:' -stderr "1. Merge (using fast-forward) '$NEW_BRANCH' to '$GIT_MAIN_BRANCH'." -stderr "2. Push '$GIT_MAIN_BRANCH'." -stderr "3. Push the '$NEW_GIT_VER_TAG' tag to '$GIT_REMOTE'." -confirm 'Those are non-reversible operations. Do you want to continue?' - -# Fast-forward main branch to release branch and push main branch. -git checkout "$GIT_MAIN_BRANCH" -git merge --ff-only "$NEW_BRANCH" -git push "$GIT_REMOTE" "$GIT_MAIN_BRANCH" - -# Push Git tag of release. -git push "$GIT_REMOTE" "$NEW_GIT_VER_TAG" - -# Create release with GitHub CLI. -if which gh >/dev/null; then - readonly GH_VERSION=$(gh --version | grep -o 'version\s\+\S\+' | \ - grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - if test $(semver_major $GH_VERSION) -ge 1; then - stderr - stderr "Creating Release 'v$NEW_VER' on GitHub." - gh release create "$NEW_GIT_VER_TAG" --title "v$NEW_VER" -F "$REL_NOTES_FILE" - else - echo "GitHub CLI >= 1.0.0 required but $GH_VERSION installed." - echo "Please create GitHub release manually." - fi -else - echo "GitHub CLI executable not found; please create GitHub release manually." -fi - -# Remove temporary file for release notes. -rm "$REL_NOTES_FILE" - -# Remove release branch, both remotely and locally. -stderr -stderr "Deleting '$NEW_BRANCH' locally and remotely." -git push "$GIT_REMOTE" --delete "$NEW_BRANCH" -git branch -d "$NEW_BRANCH" - -stderr -stderr "All done!" diff --git a/hw/vendor/pulp_platform_axi/scripts/run_verilator.sh b/hw/vendor/pulp_platform_axi/scripts/run_verilator.sh deleted file mode 100755 index a65cd091a..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/run_verilator.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# Copyright (c) 2021 ETH Zurich, University of Bologna -# -# Copyright and related rights are licensed under the Solderpad Hardware -# License, Version 0.51 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -# or agreed to in writing, software, hardware and materials distributed under -# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. - -set -e -ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) - -[ ! -z "$VERILATOR" ] || VERILATOR="verilator" - -bender script verilator -t synthesis -t synth_test > ./verilator.f - -VERILATOR_FLAGS=() -VERILATOR_FLAGS+=(-Wno-fatal) - -$VERILATOR --top-module axi_synth_bench --lint-only -f verilator.f ${VERILATOR_FLAGS[@]} diff --git a/hw/vendor/pulp_platform_axi/scripts/run_vsim.sh b/hw/vendor/pulp_platform_axi/scripts/run_vsim.sh deleted file mode 100755 index f177e77e7..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/run_vsim.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/bash -# Copyright (c) 2014-2018 ETH Zurich, University of Bologna -# -# Copyright and related rights are licensed under the Solderpad Hardware -# License, Version 0.51 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -# or agreed to in writing, software, hardware and materials distributed under -# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# -# Authors: -# - Andreas Kurth -# - Fabian Schuiki -# - Matheus Cavalcante - -set -euo pipefail -ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) - -if test -z ${VSIM+x}; then - VSIM=vsim -fi - -# Seed values for `sv_seed`; can be extended with specific values on a per-TB basis, as well as with -# a random number by passing the `--random` flag. The default value, 0, is always included to stay -# regression-consistent. -SEEDS=(0) - -call_vsim() { - for seed in ${SEEDS[@]}; do - echo "run -all" | $VSIM -sv_seed $seed "$@" | tee vsim.log 2>&1 - grep "Errors: 0," vsim.log - done -} - -exec_test() { - if [ ! -e "$ROOT/test/tb_$1.sv" ]; then - echo "Testbench for '$1' not found!" - exit 1 - fi - case "$1" in - axi_atop_filter) - for MAX_TXNS in 1 3 12; do - call_vsim tb_axi_atop_filter -gTB_N_TXNS=1000 -gTB_AXI_MAX_WRITE_TXNS=$MAX_TXNS - done - ;; - axi_cdc|axi_delayer) - call_vsim tb_$1 - ;; - axi_dw_downsizer) - for AxiSlvPortDataWidth in 8 16 32 64 128 256 512 1024; do - for (( AxiMstPortDataWidth = 8; \ - AxiMstPortDataWidth < $AxiSlvPortDataWidth; \ - AxiMstPortDataWidth *= 2 )); \ - do - call_vsim tb_axi_dw_downsizer \ - -gTbAxiSlvPortDataWidth=$AxiSlvPortDataWidth \ - -gTbAxiMstPortDataWidth=$AxiMstPortDataWidth -t 1ps - done - done - ;; - axi_dw_upsizer) - for AxiSlvPortDataWidth in 8 16 32 64 128 256 512 1024; do - for (( AxiMstPortDataWidth = $AxiSlvPortDataWidth*2; \ - AxiMstPortDataWidth <= 1024; \ - AxiMstPortDataWidth *= 2 )); \ - do - call_vsim tb_axi_dw_upsizer \ - -gTbAxiSlvPortDataWidth=$AxiSlvPortDataWidth \ - -gTbAxiMstPortDataWidth=$AxiMstPortDataWidth -t 1ps - done - done - ;; - axi_lite_regs) - SEEDS+=(10 42) - for PRIV in 0 1; do - for SECU in 0 1; do - for BYTES in 42 200 369; do - call_vsim tb_axi_lite_regs -gTbPrivProtOnly=$PRIV -gTbSecuProtOnly=$SECU \ - -gTbRegNumBytes=$BYTES -t 1ps - done - done - done - ;; - axi_lite_to_apb) - for PIPE_REQ in 0 1; do - for PIPE_RESP in 0 1; do - call_vsim tb_axi_lite_to_apb -gTbPipelineRequest=$PIPE_REQ \ - -gTbPipelineResponse=$PIPE_RESP - done - done - ;; - axi_lite_to_axi) - for DW in 8 16 32 64 128 256 512 1024; do - call_vsim tb_axi_lite_to_axi -gTB_DW=$DW -t 1ps - done - ;; - axi_sim_mem) - for AW in 16 32 64; do - for DW in 32 64 128 256 512 1024; do - call_vsim tb_axi_sim_mem -gTbAddrWidth=$AW -gTbDataWidth=$DW -t 1ps - done - done - ;; - axi_xbar) - for Atop in 0 1; do - for Exclusive in 0 1; do - for UniqueIds in 0 1; do - call_vsim tb_axi_xbar -gTbEnAtop=$Atop -gTbEnExcl=$Exclusive \ - -gTbUniqueIds=$UniqueIds - done - done - done - ;; - *) - call_vsim tb_$1 -t 1ns -coverage -voptargs="+acc +cover=bcesfx" - ;; - esac -} - -# Parse flags. -PARAMS="" -while (( "$#" )); do - case "$1" in - --random-seed) - SEEDS+=(random) - shift;; - -*--*) # unsupported flag - echo "Error: Unsupported flag '$1'." >&2 - exit 1;; - *) # preserve positional arguments - PARAMS="$PARAMS $1" - shift;; - esac -done -eval set -- "$PARAMS" - -if [ "$#" -eq 0 ]; then - tests=() - while IFS= read -r -d $'\0'; do - tb_name="$(basename -s .sv $REPLY)" - dut_name="${tb_name#tb_}" - tests+=("$dut_name") - done < <(find "$ROOT/test" -name 'tb_*.sv' -a \( ! -name '*_pkg.sv' \) -print0) -else - tests=("$@") -fi - -for t in "${tests[@]}"; do - exec_test $t -done diff --git a/hw/vendor/pulp_platform_axi/scripts/synth.sh b/hw/vendor/pulp_platform_axi/scripts/synth.sh deleted file mode 100755 index 70cb86a89..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/synth.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Copyright (c) 2014-2018 ETH Zurich, University of Bologna -# -# Copyright and related rights are licensed under the Solderpad Hardware -# License, Version 0.51 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -# or agreed to in writing, software, hardware and materials distributed under -# this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -# -# Authors: -# - Fabian Schuiki -# - Andreas Kurth - -set -e -ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) - -[ ! -z "$SYNOPSYS_DC" ] || SYNOPSYS_DC="synopsys dc_shell -64" - -echo 'remove_design -all' > ./synth.tcl -bender script synopsys -t synth_test >> ./synth.tcl -echo 'elaborate axi_synth_bench' >> ./synth.tcl - -cat ./synth.tcl | $SYNOPSYS_DC | tee synth.log 2>&1 -grep -i "warning:" synth.log || true -grep -i "error:" synth.log && false - -touch synth.completed diff --git a/hw/vendor/pulp_platform_axi/scripts/update_authors b/hw/vendor/pulp_platform_axi/scripts/update_authors deleted file mode 100755 index 30ca502aa..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/update_authors +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2021 ETH Zurich, University of Bologna -# SPDX-License-Identifier: Apache-2.0 -# -# Authors: -# - Andreas Kurth - -set -euo pipefail - -stderr() { - printf "$@" >&2 -} - -# Some authors prefer to not appear with real name and email address in author lists. -declare -A hide=( \ -) - -# Map each author name to an email address. -declare -A emails=( \ - ["Andreas Kurth"]="akurth@iis.ee.ethz.ch" \ - ["Fabian Schuiki"]="fschuiki@iis.ee.ethz.ch" \ - ["Florian Zaruba"]="zarubaf@iis.ee.ethz.ch" \ - ["Matheus Cavalcante"]="matheusd@iis.ee.ethz.ch" \ - ["Samuel Riedel"]="sriedel@iis.ee.ethz.ch" \ - ["Stefan Mach"]="smach@iis.ee.ethz.ch" \ - ["Wolfgang Rönninger"]="wroennin@iis.ee.ethz.ch" \ -) - -# Iterate over source files (see `done` line for which files are included). -while IFS= read -r -d $'\0' file; do - if $(echo "$file" | grep -q '\.svh\?$'); then # SystemVerilog file - comment_lead='//' - comment_lead_escaped='\/\/' - else - comment_lead='#' - comment_lead_escaped='#' - fi - re_authors="^\\s*${comment_lead}\\s*Authors:\$" - - # If the file does not contain an 'Authors:' paragraph, create one. - if ! grep -q "$re_authors" "$file"; then - # Find first line after copyright header. - copyright_end=$(awk "BEGIN {newparagraph=1;} - { - if (newparagraph) { - if (\$0 ~ /^${comment_lead_escaped}\\s*[cC]opyright/) { - newparagraph=0; - next - } else { - print NR; - exit - } - } else if (\$0 ~ /^\\/*$/) { - newparagraph=1; - next - } - }" "$file") - # Insert 'Authors:' header. - sed -i -e "${copyright_end}a${comment_lead} Authors:" "$file" - fi - - # Find line before and after authors list. - authors_begin=$(grep -n -m 1 "$re_authors" $file | sed 's/\([0-9]\+\).*/\1/') - authors_end=$(awk "{if (NR > $authors_begin && \$0 ~ /^\$/) {print NR; exit}}" "$file") - - # Replace author list with information from `git log`. - tmpfile=$(mktemp) - { - sed -n "1,${authors_begin}p" "$file" - readarray -t changes <<<"$(git log --follow --numstat --format='%an' "$file" | awk ' - BEGIN {totalchange=0; } - { - if (NR % 3 == 1) { - author=$0; - } else if (NR % 3 == 0) { - if (!change[author]) { - change[author]=0; - } - change[author]+=$1; - totalchange+=$1 - change[author]+=$2; - totalchange+=$2; - } - } - END { - print totalchange; - for (author in change) { - print change[author] " " author; - } - }')" - totalchange="${changes[0]}" - authorchanges=() - for authorchange in "${changes[@]:1}"; do - name="$(echo "$authorchange" | cut -d' ' -f 2-)" - change="$(echo "$authorchange" | cut -d' ' -f 1)" - # Only list authors who have contributed to more than 5% of the changes in the file. - if test $(( $change * 100 / $totalchange )) -ge 5; then - authorchanges+=("$change $name") - fi - done - # Sort author with most changes first. - readarray -t authors < <(printf '%s\n' "${authorchanges[@]}" | sort -nr) - stderr "$file\n" - stderr '%s\n' "${authors[@]}" - stderr '\n' - readarray -t authors < <(printf '%s\n' "${authors[@]}" | cut -d' ' -f2-) - for author in "${authors[@]}"; do - if ! "${hide[$author]:-false}"; then - if ! test ${emails[$author]+_}; then - stderr "Error: Email address of author '$author' unknown!\n" - exit 1 - fi - email="${emails[$author]}" - author_ascii=$(echo "$author" | sed 's/ö/oe/g') - if $(echo "$author_ascii" | grep -q -P '[^[:ascii:]]'); then - stderr "Author name '$author_ascii' contains a non-ASCII character!\n" - exit 1 - fi - echo "$comment_lead - $author_ascii <$email>" - fi - done - sed -n "$authors_end,\$p" "$file" - } > "$tmpfile" - chmod --reference="$file" "$tmpfile" # retain file permissions - mv "$tmpfile" "$file" - -done < <(git ls-tree -r -z --name-only HEAD -- 'include' 'scripts' 'src' 'test' \ - | grep -z -P '\.s(?:vh?|h)$|^(?!.+\.)') - # left alternative matches files with .sh, .sv, and .svh extensions; - # right alternative matches files with no extension diff --git a/hw/vendor/pulp_platform_axi/scripts/verilogwriter.py b/hw/vendor/pulp_platform_axi/scripts/verilogwriter.py deleted file mode 100644 index 10c340695..000000000 --- a/hw/vendor/pulp_platform_axi/scripts/verilogwriter.py +++ /dev/null @@ -1,103 +0,0 @@ -class Signal(object): - def __init__(self, name, width=0, low=0, asc=False, vec=0): - self.name = name - self.width=width - self.low = low - self.asc = asc - - def range(self): - if self.width > 0: - l = self.width+self.low-1 - r = self.low - if self.asc: - return '['+str(r)+':'+str(l)+']' - else: - return '['+str(l)+':'+str(r)+']' - return '' - -class Wire(Signal): - def write(self, width): - return 'wire{range} {name};\n'.format(range=self.range().rjust(width), name=self.name) - -class Port: - def __init__(self, name, value): - self.name = name - self.value = value - -class ModulePort(Signal): - def __init__(self, name, dir, width=0, low=0, asc=False): - super(ModulePort, self).__init__(name, width, low, asc) - self.dir = dir - - def write(self, range_width=0): - return '{dir} wire {range} {name}'.format(dir=self.dir.ljust(6), range=self.range().rjust(range_width), name=self.name) - -class Instance: - def __init__(self, module, name, parameters, ports): - self.module = module - self.name = name - self.parameters = parameters - self.ports = ports - - def write(self): - s = self.module - if self.parameters: - max_len = max([len(p.name) for p in self.parameters]) - s += '\n #(' - s += ',\n '.join(['.' + p.name.ljust(max_len) +' (' + str(p.value) + ')' for p in self.parameters]) - s += ')\n' - s += ' ' + self.name - - if self.ports: - s += '\n (' - max_len = max([len(p.name) for p in self.ports]) - s += ',\n '.join(['.' + p.name.ljust(max_len) +' (' + str(p.value) + ')' for p in self.ports]) - s += ')' - s += ';\n' - return s - -class VerilogWriter: - raw = "" - def __init__(self, name): - self.name = name - self.instances = [] - self.ports = [] - self.wires = [] - - def add(self, obj): - if isinstance(obj, Instance): - self.instances += [obj] - elif isinstance(obj, ModulePort): - self.ports += [obj] - elif isinstance(obj, Wire): - self.wires += [obj] - else: - raise Exception("Invalid type!" + str(obj)) - - def write(self, file=None): - s = ("// THIS FILE IS AUTOGENERATED BY axi_intercon_gen\n" - "// ANY MANUAL CHANGES WILL BE LOST\n") - if self.ports: - s += "`default_nettype none\n" - s += "module {name}\n".format(name=self.name) - max_len = max([len(p.range()) for p in self.ports]) - s += ' (' - s += ',\n '.join([p.write(max_len) for p in self.ports]) - s += ')' - s += ';\n\n' - if self.wires: - max_len = max([len(w.range()) for w in self.wires]) - for w in self.wires: - s += w.write(max_len + 1) - s +='\n' - s += self.raw - for i in self.instances: - s += i.write() - s += '\n' - if self.ports: - s += 'endmodule\n' - if file is None: - return s - else: - f = open(file,'w') - f.write(s) diff --git a/hw/vendor/pulp_platform_axi/src/axi_atop_filter.sv b/hw/vendor/pulp_platform_axi/src/axi_atop_filter.sv index 4a2ecff43..55ad491b2 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_atop_filter.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_atop_filter.sv @@ -40,22 +40,22 @@ module axi_atop_filter #( /// Maximum number of in-flight AXI write transactions parameter int unsigned AxiMaxWriteTxns = 0, /// AXI request type - parameter type req_t = logic, + parameter type axi_req_t = logic, /// AXI response type - parameter type resp_t = logic + parameter type axi_resp_t = logic ) ( /// Rising-edge clock of both ports - input logic clk_i, + input logic clk_i, /// Asynchronous reset, active low - input logic rst_ni, + input logic rst_ni, /// Slave port request - input req_t slv_req_i, + input axi_req_t slv_req_i, /// Slave port response - output resp_t slv_resp_o, + output axi_resp_t slv_resp_o, /// Master port request - output req_t mst_req_o, + output axi_req_t mst_req_o, /// Master port response - input resp_t mst_resp_i + input axi_resp_t mst_resp_i ); // Minimum counter width is 2 to detect underflows. @@ -67,11 +67,11 @@ module axi_atop_filter #( cnt_t w_cnt_d, w_cnt_q; typedef enum logic [2:0] { - W_FEEDTHROUGH, BLOCK_AW, ABSORB_W, HOLD_B, INJECT_B, WAIT_R + W_RESET, W_FEEDTHROUGH, BLOCK_AW, ABSORB_W, HOLD_B, INJECT_B, WAIT_R } w_state_e; w_state_e w_state_d, w_state_q; - typedef enum logic [1:0] { R_FEEDTHROUGH, INJECT_R, R_HOLD } r_state_e; + typedef enum logic [1:0] { R_RESET, R_FEEDTHROUGH, INJECT_R, R_HOLD } r_state_e; r_state_e r_state_d, r_state_q; typedef logic [AxiIdWidth-1:0] id_t; @@ -116,6 +116,8 @@ module axi_atop_filter #( w_state_d = w_state_q; unique case (w_state_q) + W_RESET: w_state_d = W_FEEDTHROUGH; + W_FEEDTHROUGH: begin // Feed AW channel through if the maximum number of outstanding bursts is not reached. if (complete_w_without_aw_downstream || (w_cnt_q.cnt < AxiMaxWriteTxns)) begin @@ -137,8 +139,8 @@ module axi_atop_filter #( mst_req_o.aw_valid = 1'b0; // Do not let AW pass to master port. slv_resp_o.aw_ready = 1'b1; // Absorb AW on slave port. id_d = slv_req_i.aw.id; // Store ID for B response. - // All atomic operations except atomic stores require a response on the R channel. - if (slv_req_i.aw.atop[5:4] != axi_pkg::ATOP_ATOMICSTORE) begin + // Some atomic operations require a response on the R channel. + if (slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // Push R response command. We do not have to wait for the ready of the register // because we know it is ready: we are its only master and will wait for the register to // be emptied before going back to the `W_FEEDTHROUGH` state. @@ -238,7 +240,7 @@ module axi_atop_filter #( end end - default: w_state_d = W_FEEDTHROUGH; + default: w_state_d = W_RESET; endcase end // Connect signals on AW and W channel that are not managed by the control FSM from slave port to @@ -266,6 +268,8 @@ module axi_atop_filter #( r_state_d = r_state_q; unique case (r_state_q) + R_RESET: r_state_d = R_FEEDTHROUGH; + R_FEEDTHROUGH: begin if (mst_resp_i.r_valid && !slv_req_i.r_ready) begin r_state_d = R_HOLD; @@ -301,7 +305,7 @@ module axi_atop_filter #( end end - default: r_state_d = R_FEEDTHROUGH; + default: r_state_d = R_RESET; endcase end // Feed all signals on AR through. @@ -329,9 +333,9 @@ module axi_atop_filter #( if (!rst_ni) begin id_q <= '0; r_beats_q <= '0; - r_state_q <= R_FEEDTHROUGH; + r_state_q <= R_RESET; w_cnt_q <= '{default: '0}; - w_state_q <= W_FEEDTHROUGH; + w_state_q <= W_RESET; end else begin id_q <= id_d; r_beats_q <= r_beats_d; @@ -405,11 +409,11 @@ module axi_atop_filter_intf #( `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -422,8 +426,8 @@ module axi_atop_filter_intf #( // Maximum number of AXI write bursts outstanding at the same time .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ), // AXI request & response type - .req_t ( req_t ), - .resp_t ( resp_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_atop_filter ( .clk_i, .rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_burst_splitter.sv b/hw/vendor/pulp_platform_axi/src/axi_burst_splitter.sv index a45584790..4f3626ebc 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_burst_splitter.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_burst_splitter.sv @@ -30,24 +30,26 @@ module axi_burst_splitter #( parameter int unsigned MaxReadTxns = 32'd0, // Maximum number of AXI write bursts outstanding at the same time parameter int unsigned MaxWriteTxns = 32'd0, + // Internal ID queue can work in two bandwidth modes: see id_queue.sv for details + parameter bit FullBW = 0, // AXI Bus Types parameter int unsigned AddrWidth = 32'd0, parameter int unsigned DataWidth = 32'd0, parameter int unsigned IdWidth = 32'd0, parameter int unsigned UserWidth = 32'd0, - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( input logic clk_i, input logic rst_ni, // Input / Slave Port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // Output / Master Port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); typedef logic [AddrWidth-1:0] addr_t; @@ -62,28 +64,27 @@ module axi_burst_splitter #( `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) // Demultiplex between supported and unsupported transactions. - req_t act_req, unsupported_req; - resp_t act_resp, unsupported_resp; - logic sel_aw_unsupported, sel_ar_unsupported; + axi_req_t act_req, unsupported_req; + axi_resp_t act_resp, unsupported_resp; + logic sel_aw_unsupported, sel_ar_unsupported; localparam int unsigned MaxTxns = (MaxReadTxns > MaxWriteTxns) ? MaxReadTxns : MaxWriteTxns; axi_demux #( - .AxiIdWidth ( IdWidth ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ), - .NoMstPorts ( 2 ), - .MaxTrans ( MaxTxns ), - .AxiLookBits ( IdWidth ), - .FallThrough ( 1'b1 ), - .SpillAw ( 1'b0 ), - .SpillW ( 1'b0 ), - .SpillB ( 1'b0 ), - .SpillAr ( 1'b0 ), - .SpillR ( 1'b0 ) + .AxiIdWidth ( IdWidth ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( MaxTxns ), + .AxiLookBits ( IdWidth ), + .SpillAw ( 1'b0 ), + .SpillW ( 1'b0 ), + .SpillB ( 1'b0 ), + .SpillAr ( 1'b0 ), + .SpillR ( 1'b0 ) ) i_demux_supported_vs_unsupported ( .clk_i, .rst_ni, @@ -119,8 +120,8 @@ module axi_burst_splitter #( // Respond to unsupported transactions with slave errors. axi_err_slv #( .AxiIdWidth ( IdWidth ), - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .Resp ( axi_pkg::RESP_SLVERR ), .ATOPs ( 1'b0 ), // The burst splitter does not support ATOPs. .MaxTrans ( 1 ) // Splitting bursts implies a low-performance bus. @@ -140,7 +141,8 @@ module axi_burst_splitter #( axi_burst_splitter_ax_chan #( .chan_t ( aw_chan_t ), .IdWidth ( IdWidth ), - .MaxTxns ( MaxWriteTxns ) + .MaxTxns ( MaxWriteTxns ), + .FullBW ( FullBW ) ) i_axi_burst_splitter_aw_chan ( .clk_i, .rst_ni, @@ -234,7 +236,8 @@ module axi_burst_splitter #( axi_burst_splitter_ax_chan #( .chan_t ( ar_chan_t ), .IdWidth ( IdWidth ), - .MaxTxns ( MaxReadTxns ) + .MaxTxns ( MaxReadTxns ), + .FullBW ( FullBW ) ) i_axi_burst_splitter_ar_chan ( .clk_i, .rst_ni, @@ -347,6 +350,7 @@ module axi_burst_splitter_ax_chan #( parameter type chan_t = logic, parameter int unsigned IdWidth = 0, parameter int unsigned MaxTxns = 0, + parameter bit FullBW = 0, parameter type id_t = logic[IdWidth-1:0] ) ( input logic clk_i, @@ -372,6 +376,7 @@ module axi_burst_splitter_ax_chan #( logic cnt_alloc_req, cnt_alloc_gnt; axi_burst_splitter_counters #( .MaxTxns ( MaxTxns ), + .FullBW ( FullBW ), .IdWidth ( IdWidth ) ) i_axi_burst_splitter_counters ( .clk_i, @@ -460,6 +465,7 @@ endmodule /// Internal module of [`axi_burst_splitter`](module.axi_burst_splitter) to order transactions. module axi_burst_splitter_counters #( parameter int unsigned MaxTxns = 0, + parameter bit FullBW = 0, parameter int unsigned IdWidth = 0, parameter type id_t = logic [IdWidth-1:0] ) ( @@ -518,7 +524,8 @@ module axi_burst_splitter_counters #( id_queue #( .ID_WIDTH ( $bits(id_t) ), .CAPACITY ( MaxTxns ), - .data_t ( cnt_idx_t ) + .data_t ( cnt_idx_t ), + .FULL_BW ( FullBW ) ) i_idq ( .clk_i, .rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_bus_compare.sv b/hw/vendor/pulp_platform_axi/src/axi_bus_compare.sv new file mode 100644 index 000000000..e1a3426ee --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_bus_compare.sv @@ -0,0 +1,724 @@ +// Copyright (c) 2019-2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +`include "axi/assign.svh" +/// Synthesizable test module comparing two AXI channels of the same type +/// This module is meant to be used in FPGA-based verification. +module axi_bus_compare #( + /// ID width of the AXI4+ATOP interface + parameter int unsigned AxiIdWidth = 32'd0, + /// FIFO depth + parameter int unsigned FifoDepth = 32'd0, + /// Consider size field in comparison + parameter bit UseSize = 1'b0, + /// Data width of the AXI4+ATOP interface + parameter int unsigned DataWidth = 32'd8, + /// AW channel type of the AXI4+ATOP interface + parameter type axi_aw_chan_t = logic, + /// W channel type of the AXI4+ATOP interface + parameter type axi_w_chan_t = logic, + /// B channel type of the AXI4+ATOP interface + parameter type axi_b_chan_t = logic, + /// AR channel type of the AXI4+ATOP interface + parameter type axi_ar_chan_t = logic, + /// R channel type of the AXI4+ATOP interface + parameter type axi_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP slave port + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type axi_rsp_t = logic, + /// ID type (*do not overwrite*) + parameter type id_t = logic [2**AxiIdWidth-1:0] +)( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4+ATOP A channel request in + input axi_req_t axi_a_req_i, + /// AXI4+ATOP A channel response out + output axi_rsp_t axi_a_rsp_o, + /// AXI4+ATOP A channel request out + output axi_req_t axi_a_req_o, + /// AXI4+ATOP A channel response in + input axi_rsp_t axi_a_rsp_i, + /// AXI4+ATOP B channel request in + input axi_req_t axi_b_req_i, + /// AXI4+ATOP B channel response out + output axi_rsp_t axi_b_rsp_o, + /// AXI4+ATOP B channel request out + output axi_req_t axi_b_req_o, + /// AXI4+ATOP B channel response in + input axi_rsp_t axi_b_rsp_i, + /// AW mismatch + output id_t aw_mismatch_o, + /// W mismatch + output logic w_mismatch_o, + /// B mismatch + output id_t b_mismatch_o, + /// AR mismatch + output id_t ar_mismatch_o, + /// R mismatch + output id_t r_mismatch_o, + /// General mismatch + output logic mismatch_o, + /// Unit is busy + output logic busy_o +); + + + //----------------------------------- + // Channel Signals + //----------------------------------- + // assign request payload A + + `AXI_ASSIGN_AW_STRUCT(axi_a_req_o.aw, axi_a_req_i.aw) + `AXI_ASSIGN_W_STRUCT(axi_a_req_o.w, axi_a_req_i.w) + `AXI_ASSIGN_AR_STRUCT(axi_a_req_o.ar, axi_a_req_i.ar) + + // assign response payload A + `AXI_ASSIGN_R_STRUCT(axi_a_rsp_o.r, axi_a_rsp_i.r) + `AXI_ASSIGN_B_STRUCT(axi_a_rsp_o.b, axi_a_rsp_i.b) + + // assign request payload B + `AXI_ASSIGN_AW_STRUCT(axi_b_req_o.aw, axi_b_req_i.aw) + `AXI_ASSIGN_W_STRUCT(axi_b_req_o.w, axi_b_req_i.w) + `AXI_ASSIGN_AR_STRUCT(axi_b_req_o.ar, axi_b_req_i.ar) + + // assign response payload B + `AXI_ASSIGN_R_STRUCT(axi_b_rsp_o.r, axi_b_rsp_i.r) + `AXI_ASSIGN_B_STRUCT(axi_b_rsp_o.b, axi_b_rsp_i.b) + + // fifo handshaking signals A + id_t fifo_valid_aw_a, fifo_ready_aw_a; + id_t fifo_valid_b_a, fifo_ready_b_a; + id_t fifo_valid_ar_a, fifo_ready_ar_a; + id_t fifo_valid_r_a, fifo_ready_r_a; + + logic fifo_sel_valid_aw_a, fifo_sel_ready_aw_a; + logic fifo_sel_valid_w_a, fifo_sel_ready_w_a; + logic fifo_sel_valid_b_a, fifo_sel_ready_b_a; + logic fifo_sel_valid_ar_a, fifo_sel_ready_ar_a; + logic fifo_sel_valid_r_a, fifo_sel_ready_r_a; + + // fifo handshaking signals B + id_t fifo_valid_aw_b, fifo_ready_aw_b; + id_t fifo_valid_b_b, fifo_ready_b_b; + id_t fifo_valid_ar_b, fifo_ready_ar_b; + id_t fifo_valid_r_b, fifo_ready_r_b; + + logic fifo_sel_valid_aw_b, fifo_sel_ready_aw_b; + logic fifo_sel_valid_w_b, fifo_sel_ready_w_b; + logic fifo_sel_valid_b_b, fifo_sel_ready_b_b; + logic fifo_sel_valid_ar_b, fifo_sel_ready_ar_b; + logic fifo_sel_valid_r_b, fifo_sel_ready_r_b; + + + //----------------------------------- + // FIFO Output Signals + //----------------------------------- + id_t fifo_cmp_valid_aw_a; + logic fifo_cmp_valid_w_a; + id_t fifo_cmp_valid_b_a; + id_t fifo_cmp_valid_ar_a; + id_t fifo_cmp_valid_r_a; + + id_t fifo_cmp_valid_aw_b; + logic fifo_cmp_valid_w_b; + id_t fifo_cmp_valid_b_b; + id_t fifo_cmp_valid_ar_b; + id_t fifo_cmp_valid_r_b; + + axi_aw_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_aw_a; + axi_w_chan_t fifo_cmp_data_w_a; + axi_b_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_b_a; + axi_ar_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_ar_a; + axi_r_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_r_a; + + axi_aw_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_aw_b; + axi_w_chan_t fifo_cmp_data_w_b; + axi_b_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_b_b; + axi_ar_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_ar_b; + axi_r_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_r_b; + + + // Size alignment signals + logic [2:0] w_size; + logic [$clog2(DataWidth/8)-1:0] w_lower, w_offset, w_increment; + logic [2**AxiIdWidth-1:0][2:0] r_size; + logic [2**AxiIdWidth-1:0][$clog2(DataWidth/8)-1:0] r_lower, r_offset, r_increment; + + //----------------------------------- + // Channel A stream forks + //----------------------------------- + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.aw_valid ), + .ready_o ( axi_a_rsp_o.aw_ready ), + .valid_o ( {fifo_sel_valid_aw_a, axi_a_req_o.aw_valid} ), + .ready_i ( {fifo_sel_ready_aw_a, axi_a_rsp_i.aw_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.w_valid ), + .ready_o ( axi_a_rsp_o.w_ready ), + .valid_o ( {fifo_sel_valid_w_a, axi_a_req_o.w_valid} ), + .ready_i ( {fifo_sel_ready_w_a, axi_a_rsp_i.w_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_b_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_rsp_i.b_valid ), + .ready_o ( axi_a_req_o.b_ready ), + .valid_o ( {fifo_sel_valid_b_a, axi_a_rsp_o.b_valid} ), + .ready_i ( {fifo_sel_ready_b_a, axi_a_req_i.b_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.ar_valid ), + .ready_o ( axi_a_rsp_o.ar_ready ), + .valid_o ( {fifo_sel_valid_ar_a, axi_a_req_o.ar_valid} ), + .ready_i ( {fifo_sel_ready_ar_a, axi_a_rsp_i.ar_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_r_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_rsp_i.r_valid ), + .ready_o ( axi_a_req_o.r_ready ), + .valid_o ( {fifo_sel_valid_r_a, axi_a_rsp_o.r_valid} ), + .ready_i ( {fifo_sel_ready_r_a, axi_a_req_i.r_ready} ) + ); + + + //----------------------------------- + // Channel A FIFOs + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_fifos_a + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_aw_chan_t ) + ) i_stream_fifo_aw_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.aw ), + .valid_i ( fifo_valid_aw_a [id] ), + .ready_o ( fifo_ready_aw_a [id] ), + .data_o ( fifo_cmp_data_aw_a [id] ), + .valid_o ( fifo_cmp_valid_aw_a [id] ), + .ready_i ( fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_b_chan_t ) + ) i_stream_fifo_b_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_rsp_i.b ), + .valid_i ( fifo_valid_b_a [id] ), + .ready_o ( fifo_ready_b_a [id] ), + .data_o ( fifo_cmp_data_b_a [id] ), + .valid_o ( fifo_cmp_valid_b_a [id] ), + .ready_i ( fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_ar_chan_t ) + ) i_stream_fifo_ar_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.ar ), + .valid_i ( fifo_valid_ar_a [id] ), + .ready_o ( fifo_ready_ar_a [id] ), + .data_o ( fifo_cmp_data_ar_a [id] ), + .valid_o ( fifo_cmp_valid_ar_a [id] ), + .ready_i ( fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id] ) + ); + + if (UseSize) begin : gen_r_size + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( $clog2(DataWidth/8)+3 ), + // .DATA_WIDTH ( 7+3 ), + .DEPTH ( 2*FifoDepth ) + ) i_stream_fifo_w_size ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o (), + .data_i ( {axi_a_req_i.ar.addr[$clog2(DataWidth/8)-1:0], axi_a_req_i.ar.size} ), + .valid_i ( fifo_valid_ar_a [id] & fifo_ready_ar_a [id] ), + .ready_o (), + .data_o ( {r_offset[id], r_size[id]} ), + .valid_o (), + .ready_i ( fifo_cmp_valid_r_a[id] & fifo_cmp_valid_r_b[id] & fifo_cmp_data_r_a[id].last ) + ); + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_r_increment + if(!rst_ni) begin + r_increment[id] <= '0; + end else begin + if (fifo_cmp_valid_r_a[id] && fifo_cmp_valid_r_b[id]) begin + if (fifo_cmp_data_r_a[id].last) begin + r_increment[id] <= '0; + end else begin + r_increment[id] <= r_increment[id] + 2**r_size[id] - ((r_offset[id]+r_increment[id])%(2**r_size[id])); + end + end + end + end + assign r_lower[id] = r_offset[id] + r_increment[id]; + end else begin : gen_no_size + assign r_offset[id] = '0; + assign r_size[id] = '1; + assign r_lower[id] = '0; + end + + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_r_chan_t ) + ) i_stream_fifo_r_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_rsp_i.r ), + .valid_i ( fifo_valid_r_a [id] ), + .ready_o ( fifo_ready_r_a [id] ), + .data_o ( fifo_cmp_data_r_a [id] ), + .valid_o ( fifo_cmp_valid_r_a [id] ), + .ready_i ( fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id] ) + ); + end + + if (UseSize) begin : gen_w_size + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( $clog2(DataWidth/8)+3 ), + // .DATA_WIDTH ( 7+3 ), + .DEPTH ( FifoDepth ) + ) i_stream_fifo_w_size ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o (), + .data_i ( {axi_a_req_i.aw.addr[$clog2(DataWidth/8)-1:0], axi_a_req_i.aw.size} ), + .valid_i ( axi_a_req_i.aw_valid & axi_a_rsp_o.aw_ready ), + .ready_o (), + .data_o ( {w_offset, w_size} ), + .valid_o (), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b & fifo_cmp_data_w_a.last ) + ); + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_w_increment + if(!rst_ni) begin + w_increment <= '0; + end else begin + if (fifo_cmp_valid_w_a && fifo_cmp_valid_w_b) begin + if (fifo_cmp_data_w_a.last) begin + w_increment <= '0; + end else begin + w_increment <= (w_increment + 2**w_size) - ((w_offset+w_increment)%(2**w_size)); + end + end + end + end + assign w_lower = w_offset + w_increment; + end else begin : gen_no_size + assign w_offset = '0; + assign w_size = '1; + assign w_lower = '0; + end + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_w_chan_t ) + ) i_stream_fifo_w_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.w ), + .valid_i ( fifo_sel_valid_w_a ), + .ready_o ( fifo_sel_ready_w_a ), + .data_o ( fifo_cmp_data_w_a ), + .valid_o ( fifo_cmp_valid_w_a ), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) + ); + + + //----------------------------------- + // Input Handshaking A + //----------------------------------- + always_comb begin : gen_handshaking_a + // aw + // defaults + fifo_valid_aw_a = '0; + fifo_sel_ready_aw_a = '0; + // assign according id + fifo_valid_aw_a [axi_a_req_i.aw.id] = fifo_sel_valid_aw_a; + fifo_sel_ready_aw_a = fifo_ready_aw_a[axi_a_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_a = '0; + fifo_sel_ready_b_a = '0; + // assign according id + fifo_valid_b_a [axi_a_rsp_i.b.id] = fifo_sel_valid_b_a; + fifo_sel_ready_b_a = fifo_ready_b_a[axi_a_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_a = '0; + fifo_sel_ready_ar_a = '0; + // assign according id + fifo_valid_ar_a [axi_a_req_i.ar.id] = fifo_sel_valid_ar_a; + fifo_sel_ready_ar_a = fifo_ready_ar_a[axi_a_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_a = '0; + fifo_sel_ready_r_a = '0; + // assign according id + fifo_valid_r_a [axi_a_rsp_i.r.id] = fifo_sel_valid_r_a; + fifo_sel_ready_r_a = fifo_ready_r_a[axi_a_rsp_i.r.id]; + end + + + //----------------------------------- + // Channel B stream forks + //----------------------------------- + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.aw_valid ), + .ready_o ( axi_b_rsp_o.aw_ready ), + .valid_o ( {fifo_sel_valid_aw_b, axi_b_req_o.aw_valid} ), + .ready_i ( {fifo_sel_ready_aw_b, axi_b_rsp_i.aw_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.w_valid ), + .ready_o ( axi_b_rsp_o.w_ready ), + .valid_o ( {fifo_sel_valid_w_b, axi_b_req_o.w_valid} ), + .ready_i ( {fifo_sel_ready_w_b, axi_b_rsp_i.w_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_b_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_rsp_i.b_valid ), + .ready_o ( axi_b_req_o.b_ready ), + .valid_o ( {fifo_sel_valid_b_b, axi_b_rsp_o.b_valid} ), + .ready_i ( {fifo_sel_ready_b_b, axi_b_req_i.b_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.ar_valid ), + .ready_o ( axi_b_rsp_o.ar_ready ), + .valid_o ( {fifo_sel_valid_ar_b, axi_b_req_o.ar_valid} ), + .ready_i ( {fifo_sel_ready_ar_b, axi_b_rsp_i.ar_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_r_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_rsp_i.r_valid ), + .ready_o ( axi_b_req_o.r_ready ), + .valid_o ( {fifo_sel_valid_r_b, axi_b_rsp_o.r_valid} ), + .ready_i ( {fifo_sel_ready_r_b, axi_b_req_i.r_ready} ) + ); + + + //----------------------------------- + // Channel B FIFOs + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_fifos_b + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_aw_chan_t ) + ) i_stream_fifo_aw_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.aw ), + .valid_i ( fifo_valid_aw_b [id] ), + .ready_o ( fifo_ready_aw_b [id] ), + .data_o ( fifo_cmp_data_aw_b [id] ), + .valid_o ( fifo_cmp_valid_aw_b [id] ), + .ready_i ( fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_b_chan_t ) + ) i_stream_fifo_b_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_rsp_i.b ), + .valid_i ( fifo_valid_b_b [id] ), + .ready_o ( fifo_ready_b_b [id] ), + .data_o ( fifo_cmp_data_b_b [id] ), + .valid_o ( fifo_cmp_valid_b_b [id] ), + .ready_i ( fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_ar_chan_t ) + ) i_stream_fifo_ar_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.ar ), + .valid_i ( fifo_valid_ar_b [id] ), + .ready_o ( fifo_ready_ar_b [id] ), + .data_o ( fifo_cmp_data_ar_b [id] ), + .valid_o ( fifo_cmp_valid_ar_b [id] ), + .ready_i ( fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_r_chan_t ) + ) i_stream_fifo_r_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_rsp_i.r ), + .valid_i ( fifo_valid_r_b [id] ), + .ready_o ( fifo_ready_r_b [id] ), + .data_o ( fifo_cmp_data_r_b [id] ), + .valid_o ( fifo_cmp_valid_r_b [id] ), + .ready_i ( fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id] ) + ); + end + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_w_chan_t ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.w ), + .valid_i ( fifo_sel_valid_w_b ), + .ready_o ( fifo_sel_ready_w_b ), + .data_o ( fifo_cmp_data_w_b ), + .valid_o ( fifo_cmp_valid_w_b ), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) + ); + + + //----------------------------------- + // Input Handshaking B + //----------------------------------- + always_comb begin : gen_handshaking_b + // aw + // defaults + fifo_valid_aw_b = '0; + fifo_sel_ready_aw_b = '0; + // assign according id + fifo_valid_aw_b [axi_b_req_i.aw.id] = fifo_sel_valid_aw_b; + fifo_sel_ready_aw_b = fifo_ready_aw_b[axi_b_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_b = '0; + fifo_sel_ready_b_b = '0; + // assign according id + fifo_valid_b_b [axi_b_rsp_i.b.id] = fifo_sel_valid_b_b; + fifo_sel_ready_b_b = fifo_ready_b_b[axi_b_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_b = '0; + fifo_sel_ready_ar_b = '0; + // assign according id + fifo_valid_ar_b [axi_b_req_i.ar.id] = fifo_sel_valid_ar_b; + fifo_sel_ready_ar_b = fifo_ready_ar_b[axi_b_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_b = '0; + fifo_sel_ready_r_b = '0; + // assign according id + fifo_valid_r_b [axi_b_rsp_i.r.id] = fifo_sel_valid_r_b; + fifo_sel_ready_r_b = fifo_ready_r_b[axi_b_rsp_i.r.id]; + end + + + //----------------------------------- + // Comparison + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_cmp + logic [DataWidth/8-1:0] r_data_partial_mismatch; + logic r_data_mismatch; + + if (UseSize) begin : gen_r_mismatch_sized + for (genvar j = 0; j < DataWidth/8; j++) begin : gen_r_partial_mismatch + assign r_data_partial_mismatch[j] = fifo_cmp_data_r_a[id].data[8*j+:8] != fifo_cmp_data_r_b[id].data[8*j+:8]; + end + + always_comb begin : proc_r_data_mismatch + r_data_mismatch = '0; + for (int unsigned j = 0; j < DataWidth/8; j++) begin + if (j >= r_lower[id] && j < (r_lower[id] + 2**r_size[id])-((r_lower[id] + 2**r_size[id])%(2**r_size[id])) ) begin + r_data_mismatch |= r_data_partial_mismatch[j]; + end + end + end + end else begin : gen_r_mismatch_unsized + assign r_data_mismatch = fifo_cmp_data_r_a[id].data != fifo_cmp_data_r_b[id].data; + end + + assign aw_mismatch_o [id] = (fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id]) ? + fifo_cmp_data_aw_a [id] != fifo_cmp_data_aw_b [id] : '0; + assign b_mismatch_o [id] = (fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id]) ? + fifo_cmp_data_b_a [id] != fifo_cmp_data_b_b [id] : '0; + assign ar_mismatch_o [id] = (fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id]) ? + fifo_cmp_data_ar_a [id] != fifo_cmp_data_ar_b [id] : '0; + assign r_mismatch_o [id] = (fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id]) ? + ( fifo_cmp_data_r_a[id].id != fifo_cmp_data_r_b[id].id | + r_data_mismatch | + fifo_cmp_data_r_a[id].resp != fifo_cmp_data_r_b[id].resp | + fifo_cmp_data_r_a[id].last != fifo_cmp_data_r_b[id].last | + fifo_cmp_data_r_a[id].user != fifo_cmp_data_r_b[id].user ) + : '0; + end + + logic [DataWidth/8-1:0] w_data_partial_mismatch; + logic w_data_mismatch; + + if (UseSize) begin : gen_w_mismatch_sized + for (genvar j = 0; j < DataWidth/8; j++) begin : gen_w_partial_mismatch + assign w_data_partial_mismatch[j] = fifo_cmp_data_w_a.data[8*j+:8] != fifo_cmp_data_w_b.data[8*j+:8] | + fifo_cmp_data_w_a.strb[ j ] != fifo_cmp_data_w_b.strb[ j ]; + end + + always_comb begin : proc_w_data_mismatch + w_data_mismatch = '0; + for (int unsigned j = 0; j < DataWidth/8; j++) begin + if (j >= w_lower && j < (w_lower + 2**w_size)-((w_lower + 2**w_size)%(2**w_size)) ) begin + w_data_mismatch |= w_data_partial_mismatch[j]; + end + end + end + end else begin : gen_w_mismatch_unsized + assign w_data_mismatch = fifo_cmp_data_w_a.data != fifo_cmp_data_w_b.data | + fifo_cmp_data_w_a.strb != fifo_cmp_data_w_b.strb; + end + + assign w_mismatch_o = (fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) ? + ( w_data_mismatch | + fifo_cmp_data_w_a.last != fifo_cmp_data_w_b.last | + fifo_cmp_data_w_a.user != fifo_cmp_data_w_b.user ) + : '0; + + + //----------------------------------- + // Outputs + //----------------------------------- + assign busy_o = (|fifo_cmp_valid_aw_a) | (|fifo_cmp_valid_aw_b) | + (|fifo_cmp_valid_w_a) | (|fifo_cmp_valid_w_b) | + (|fifo_cmp_valid_b_a) | (|fifo_cmp_valid_b_b) | + (|fifo_cmp_valid_ar_a) | (|fifo_cmp_valid_ar_b) | + (|fifo_cmp_valid_r_a) | (|fifo_cmp_valid_r_b); + + + assign mismatch_o = (|aw_mismatch_o) | (|w_mismatch_o) | (|b_mismatch_o) | + (|ar_mismatch_o) | (|r_mismatch_o); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_cdc.sv b/hw/vendor/pulp_platform_axi/src/axi_cdc.sv index 1e422ed72..81ad58818 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_cdc.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_cdc.sv @@ -11,10 +11,8 @@ // // Authors: // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba +// - Luca Valente // - Wolfgang Roenninger -// - Luca Valente `include "axi/assign.svh" @@ -32,7 +30,9 @@ module axi_cdc #( parameter type axi_req_t = logic, // encapsulates request channels parameter type axi_resp_t = logic, // encapsulates request channels /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LogDepth = 1 + parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -65,7 +65,8 @@ module axi_cdc #( .r_chan_t ( r_chan_t ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), - .LogDepth ( LogDepth ) + .LogDepth ( LogDepth ), + .SyncStages ( SyncStages ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, @@ -96,7 +97,8 @@ module axi_cdc #( .r_chan_t ( r_chan_t ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), - .LogDepth ( LogDepth ) + .LogDepth ( LogDepth ), + .SyncStages ( SyncStages ) ) i_axi_cdc_dst ( .dst_clk_i, .dst_rst_ni, @@ -131,7 +133,9 @@ module axi_cdc_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -173,7 +177,8 @@ module axi_cdc_intf #( .r_chan_t ( r_chan_t ), .axi_req_t ( req_t ), .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc ( .src_clk_i, .src_rst_ni, @@ -191,7 +196,9 @@ module axi_lite_cdc_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -231,7 +238,8 @@ module axi_lite_cdc_intf #( .r_chan_t ( r_chan_t ), .axi_req_t ( req_t ), .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc ( .src_clk_i, .src_rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_cdc_dst.sv b/hw/vendor/pulp_platform_axi/src/axi_cdc_dst.sv index d365156ce..c3e76bfc1 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_cdc_dst.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_cdc_dst.sv @@ -10,10 +10,8 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Luca Valente // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba -// - Luca Valente `include "axi/assign.svh" `include "axi/typedef.svh" @@ -26,6 +24,8 @@ module axi_cdc_dst #( /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -58,8 +58,15 @@ module axi_cdc_dst #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA + // Workaround for a bug in Questa: Pass flat logic vector instead of struct to type parameter. .T ( logic [$bits(aw_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + // Other tools, such as VCS, have problems with type parameters constructed through `$bits()`. + .T ( aw_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_aw ( .async_data_i ( async_data_slave_aw_data_i ), .async_wptr_i ( async_data_slave_aw_wptr_i ), @@ -72,8 +79,13 @@ module axi_cdc_dst #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(w_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( w_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_w ( .async_data_i ( async_data_slave_w_data_i ), .async_wptr_i ( async_data_slave_w_wptr_i ), @@ -86,8 +98,13 @@ module axi_cdc_dst #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(b_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( b_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_b ( .src_clk_i ( dst_clk_i ), .src_rst_ni ( dst_rst_ni ), @@ -100,8 +117,13 @@ module axi_cdc_dst #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(ar_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( ar_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_ar ( .dst_clk_i, .dst_rst_ni, @@ -114,8 +136,13 @@ module axi_cdc_dst #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(r_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( r_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_r ( .src_clk_i ( dst_clk_i ), .src_rst_ni ( dst_rst_ni ), @@ -136,7 +163,9 @@ module axi_cdc_dst_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // asynchronous slave port AXI_BUS_ASYNC_GRAY.Slave src, @@ -163,14 +192,15 @@ module axi_cdc_dst_intf #( resp_t dst_resp; axi_cdc_dst #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_dst ( .async_data_slave_aw_data_i ( src.aw_data ), .async_data_slave_aw_wptr_i ( src.aw_wptr ), @@ -203,7 +233,9 @@ module axi_lite_cdc_dst_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // asynchronous slave port AXI_LITE_ASYNC_GRAY.Slave src, @@ -228,14 +260,15 @@ module axi_lite_cdc_dst_intf #( resp_t dst_resp; axi_cdc_dst #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_dst ( .async_data_slave_aw_data_i ( src.aw_data ), .async_data_slave_aw_wptr_i ( src.aw_wptr ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_cdc_src.sv b/hw/vendor/pulp_platform_axi/src/axi_cdc_src.sv index 1065640ac..36e4e6119 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_cdc_src.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_cdc_src.sv @@ -10,10 +10,8 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Luca Valente // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba -// - Luca Valente `include "axi/assign.svh" `include "axi/typedef.svh" @@ -25,7 +23,9 @@ /// the FIFO; see the header of `cdc_fifo_gray` for instructions. module axi_cdc_src #( /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LogDepth = 1, + parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -58,8 +58,14 @@ module axi_cdc_src #( ); cdc_fifo_gray_src #( + // Workaround for a bug in Questa (see comment in `axi_cdc_dst` for details). +`ifdef QUESTA .T ( logic [$bits(aw_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( aw_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_aw ( .src_clk_i, .src_rst_ni, @@ -72,8 +78,13 @@ module axi_cdc_src #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(w_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( w_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_w ( .src_clk_i, .src_rst_ni, @@ -86,8 +97,13 @@ module axi_cdc_src #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(b_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( b_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_b ( .dst_clk_i ( src_clk_i ), .dst_rst_ni ( src_rst_ni ), @@ -100,8 +116,13 @@ module axi_cdc_src #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(ar_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( ar_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_ar ( .src_clk_i, .src_rst_ni, @@ -114,8 +135,13 @@ module axi_cdc_src #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(r_chan_t)-1:0] ), - .LOG_DEPTH ( LogDepth ) +`else + .T ( r_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_r ( .dst_clk_i ( src_clk_i ), .dst_rst_ni ( src_rst_ni ), @@ -136,7 +162,9 @@ module axi_cdc_src_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // synchronous slave port - clocked by `src_clk_i` input logic src_clk_i, @@ -166,14 +194,15 @@ module axi_cdc_src_intf #( `AXI_ASSIGN_FROM_RESP(src, src_resp) axi_cdc_src #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, @@ -203,7 +232,9 @@ module axi_lite_cdc_src_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // synchronous slave port - clocked by `src_clk_i` input logic src_clk_i, @@ -231,19 +262,20 @@ module axi_lite_cdc_src_intf #( `AXI_LITE_ASSIGN_FROM_RESP(src, src_resp) axi_cdc_src #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, - .src_req_o ( src_req ), - .src_resp_i ( src_resp ), + .src_req_i ( src_req ), + .src_resp_o ( src_resp ), .async_data_master_aw_data_o ( dst.aw_data ), .async_data_master_aw_wptr_o ( dst.aw_wptr ), .async_data_master_aw_rptr_i ( dst.aw_rptr ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_chan_compare.sv b/hw/vendor/pulp_platform_axi/src/axi_chan_compare.sv new file mode 100644 index 000000000..1e4e82347 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_chan_compare.sv @@ -0,0 +1,266 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz +// - Paul Scheffler +// - Tim Fischer + +/// Non-synthesizable module comparing two AXI channels of the same type +module axi_chan_compare #( + /// Ignore ID field if it was remapped + parameter bit IgnoreId = 1'b0, + /// Allow reordered responses of different IDs, + /// not compatible with `IgnoreId` + parameter bit AllowReordering = 1'b0, + /// AXI ID Width + parameter int unsigned IdWidth = 1, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type req_t = logic, + parameter type resp_t = logic +)( + input logic clk_a_i, + input logic clk_b_i, + input req_t axi_a_req, + input resp_t axi_a_res, + input req_t axi_b_req, + input resp_t axi_b_res +); + + function automatic void print_aw ( + input aw_chan_t aw_expected, + input aw_chan_t aw_received + ); + // verilog_lint: waive-start line-length + $display("AW | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", aw_expected.id, aw_received.id); + $display("addr: | %64x | %64x", aw_expected.addr, aw_received.addr); + $display("len: | %64d | %64d", aw_expected.len, aw_received.len); + $display("size: | %64d | %64d", aw_expected.size, aw_received.size); + $display("burst: | %64d | %64d", aw_expected.burst, aw_received.burst); + $display("lock: | %64d | %64d", aw_expected.lock, aw_received.lock); + $display("cache: | %64d | %64d", aw_expected.cache, aw_received.cache); + $display("prot: | %64d | %64d", aw_expected.prot, aw_received.prot); + $display("qos: | %64d | %64d", aw_expected.qos, aw_received.qos); + $display("region: | %64d | %64d", aw_expected.region, aw_received.region); + $display("user: | %64d | %64d", aw_expected.user, aw_received.user); + $display("atop: | %64d | %64d", aw_expected.atop, aw_received.atop); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_ar ( + input ar_chan_t ar_expected, + input ar_chan_t ar_received + ); + // verilog_lint: waive-start line-length + $display("AR | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", ar_expected.id, ar_received.id); + $display("addr: | %64x | %64x", ar_expected.addr, ar_received.addr); + $display("len: | %64d | %64d", ar_expected.len, ar_received.len); + $display("size: | %64d | %64d", ar_expected.size, ar_received.size); + $display("burst: | %64d | %64d", ar_expected.burst, ar_received.burst); + $display("lock: | %64d | %64d", ar_expected.lock, ar_received.lock); + $display("cache: | %64d | %64d", ar_expected.cache, ar_received.cache); + $display("prot: | %64d | %64d", ar_expected.prot, ar_received.prot); + $display("qos: | %64d | %64d", ar_expected.qos, ar_received.qos); + $display("region: | %64d | %64d", ar_expected.region, ar_received.region); + $display("user: | %64d | %64d", ar_expected.user, ar_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_w ( + input w_chan_t w_expected, + input w_chan_t w_received + ); + // verilog_lint: waive-start line-length + $display("W | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("data: | %64x | %64x", w_expected.data, w_received.data); + $display("strb: | %64d | %64d", w_expected.strb, w_received.strb); + $display("last: | %64d | %64d", w_expected.last, w_received.last); + $display("user: | %64d | %64d", w_expected.user, w_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_b ( + input b_chan_t b_expected, + input b_chan_t b_received + ); + // verilog_lint: waive-start line-length + $display("B | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", b_expected.id, b_received.id); + $display("resp: | %64d | %64d", b_expected.resp, b_received.resp); + $display("user: | %64d | %64d", b_expected.user, b_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_r ( + input r_chan_t r_expected, + input r_chan_t r_received + ); + // verilog_lint: waive-start line-length + $display("R | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", r_expected.id, r_received.id); + $display("data: | %64x | %64x", r_expected.data, r_received.data); + $display("resp: | %64d | %64d", r_expected.resp, r_received.resp); + $display("last: | %64d | %64d", r_expected.last, r_received.last); + $display("user: | %64d | %64d", r_expected.user, r_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + localparam NumIds = (AllowReordering)? 2**IdWidth : 1; + + // queues + aw_chan_t aw_queue [NumIds-1:0][$]; + w_chan_t w_queue [$]; + b_chan_t b_queue [NumIds-1:0][$]; + ar_chan_t ar_queue [NumIds-1:0][$]; + r_chan_t r_queue [NumIds-1:0][$]; + + // requests generated at axi A: enqueue elements + always_ff @(posedge clk_a_i) begin : proc_enqueue_a + // aw + if (axi_a_req.aw_valid & axi_a_res.aw_ready) + if (AllowReordering) aw_queue[axi_a_req.aw.id].push_back(axi_a_req.aw); + else aw_queue[0].push_back(axi_a_req.aw); + // w + if (axi_a_req.w_valid & axi_a_res.w_ready) + w_queue.push_back(axi_a_req.w); + // ar + if (axi_a_req.ar_valid & axi_a_res.ar_ready) + if (AllowReordering) ar_queue[axi_a_req.ar.id].push_back(axi_a_req.ar); + else ar_queue[0].push_back(axi_a_req.ar); + end + + // responses generated at axi B: enqueue elements + always_ff @(posedge clk_b_i) begin : proc_enqueue_b + // b + if (axi_b_res.b_valid & axi_b_req.b_ready) + if (AllowReordering) b_queue[axi_b_res.b.id].push_back(axi_b_res.b); + else b_queue[0].push_back(axi_b_res.b); + // r + if (axi_b_res.r_valid & axi_b_req.r_ready) + if (AllowReordering) r_queue[axi_b_res.r.id].push_back(axi_b_res.r); + else r_queue[0].push_back(axi_b_res.r); + end + + // requests arriving at axi B from A: dequeue elements and check + always_ff @(posedge clk_b_i) begin : proc_dequeue_and_check_b + // aw + if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin + automatic aw_chan_t aw_exp, aw_recv; + if (AllowReordering) begin + if (aw_queue[axi_b_req.aw.id].size() == 0) $error("AW queue is empty!"); + aw_exp = aw_queue[axi_b_req.aw.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (aw_queue[0].size() == 0) $error("AW queue is empty!"); + aw_exp = aw_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + aw_recv = axi_b_req.aw; + if (IgnoreId) begin + aw_exp.id = 'X; + aw_recv.id = 'X; + end + if (aw_exp !== aw_recv) begin + $error("AW mismatch!"); + print_aw(aw_exp, aw_recv); + end + end + // w + if (axi_b_req.w_valid & axi_b_res.w_ready) begin + automatic w_chan_t w_exp, w_recv; + if (w_queue.size() == 0) $error("W queue is empty!"); + w_exp = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + w_recv = axi_b_req.w; + if (w_exp !== w_recv) begin + $error("W mismatch!"); + print_w(w_exp, w_recv); + end + end + // ar + if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin + automatic ar_chan_t ar_exp, ar_recv; + if (AllowReordering) begin + if (ar_queue[axi_b_req.ar.id].size() == 0) $error("AR queue is empty!"); + ar_exp = ar_queue[axi_b_req.ar.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (ar_queue[0].size() == 0) $error("AR queue is empty!"); + ar_exp = ar_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + ar_recv = axi_b_req.ar; + if (IgnoreId) begin + ar_exp.id = 'X; + ar_recv.id = 'X; + end + if (ar_exp !== ar_recv) begin + $error("AR mismatch!"); + print_ar(ar_exp, ar_recv); + end + end + end + + // responses arriving at axi A from B: dequeue elements and check + always_ff @(posedge clk_a_i) begin : proc_dequeue_and_check_a + // b + if (axi_a_res.b_valid & axi_a_req.b_ready) begin + automatic b_chan_t b_exp, b_recv; + if (AllowReordering) begin + if (b_queue[axi_a_res.b.id].size() == 0) $error("B queue is empty!"); + b_exp = b_queue[axi_a_res.b.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (b_queue[0].size() == 0) $error("B queue is empty!"); + b_exp = b_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + b_recv = axi_a_res.b; + if (IgnoreId) begin + b_exp.id = 'X; + b_recv.id = 'X; + end + if (b_exp !== b_recv) begin + $error("B mismatch!"); + print_b(b_exp, b_recv); + end + end + // r + if (axi_a_res.r_valid & axi_a_req.r_ready) begin + automatic r_chan_t r_exp, r_recv; + if (AllowReordering) begin + if (r_queue[axi_a_res.r.id].size() == 0) $error("R queue is empty!"); + r_exp = r_queue[axi_a_res.r.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (r_queue[0].size() == 0) $error("R queue is empty!"); + r_exp = r_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + r_recv = axi_a_res.r; + if (IgnoreId) begin + r_exp.id = 'X; + r_recv.id = 'X; + end + if (r_exp !== r_recv) begin + $error("R mismatch!"); + print_r(r_exp, r_recv); + end + end + end + +endmodule : axi_chan_compare diff --git a/hw/vendor/pulp_platform_axi/src/axi_cut.sv b/hw/vendor/pulp_platform_axi/src/axi_cut.sv index 6c31321b6..34278ca62 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_cut.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_cut.sv @@ -19,25 +19,25 @@ /// Breaks all combinatorial paths between its input and output. module axi_cut #( // bypass enable - parameter bit Bypass = 1'b0, + parameter bit Bypass = 1'b0, // AXI channel structs - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, // AXI request & response structs - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( - input logic clk_i, - input logic rst_ni, + input logic clk_i, + input logic rst_ni, // salve port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // master port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); // a spill register for each channel @@ -145,11 +145,11 @@ module axi_cut_intf #( `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, in) `AXI_ASSIGN_FROM_RESP(in, slv_resp) @@ -158,14 +158,14 @@ module axi_cut_intf #( `AXI_ASSIGN_TO_RESP(mst_resp, out) axi_cut #( - .Bypass ( BYPASS ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .Bypass ( BYPASS ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_cut ( .clk_i, .rst_ni, @@ -219,11 +219,11 @@ module axi_lite_cut_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_LITE_ASSIGN_TO_REQ(slv_req, in) `AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp) @@ -232,14 +232,14 @@ module axi_lite_cut_intf #( `AXI_LITE_ASSIGN_TO_RESP(mst_resp, out) axi_cut #( - .Bypass ( BYPASS ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .Bypass ( BYPASS ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_cut ( .clk_i, .rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_delayer.sv b/hw/vendor/pulp_platform_axi/src/axi_delayer.sv index cab18eb59..8d217d14e 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_delayer.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_delayer.sv @@ -16,28 +16,28 @@ /// Synthesizable module that (randomly) delays AXI channels. module axi_delayer #( // AXI channel types - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, // AXI request & response types - parameter type req_t = logic, - parameter type resp_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, // delay parameters parameter bit StallRandomInput = 0, parameter bit StallRandomOutput = 0, parameter int unsigned FixedDelayInput = 1, parameter int unsigned FixedDelayOutput = 1 ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low // slave port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // master port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); // AW stream_delay #( @@ -152,11 +152,11 @@ module axi_delayer_intf #( `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -170,8 +170,8 @@ module axi_delayer_intf #( .b_chan_t ( b_chan_t ), .ar_chan_t ( ar_chan_t ), .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .StallRandomInput ( STALL_RANDOM_INPUT ), .StallRandomOutput ( STALL_RANDOM_OUTPUT ), .FixedDelayInput ( FIXED_DELAY_INPUT ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_demux.sv b/hw/vendor/pulp_platform_axi/src/axi_demux.sv index 270ee05d9..899d83521 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_demux.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_demux.sv @@ -9,27 +9,50 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Michael Rogenmoser // - Wolfgang Roenninger +// - Thomas Benz // - Andreas Kurth +`include "common_cells/assertions.svh" `include "common_cells/registers.svh" -// axi_demux: Demultiplex an AXI bus from one slave port to multiple master ports. -// See `doc/axi_demux.md` for the documentation, including the definition of parameters and ports. +`ifdef QUESTA +// Derive `TARGET_VSIM`, which is used for tool-specific workarounds in this file, from `QUESTA`, +// which is automatically set in Questa. +`define TARGET_VSIM +`endif + +/// Demultiplex one AXI4+ATOP slave port to multiple AXI4+ATOP master ports. +/// +/// The AW and AR slave channels each have a `select` input to determine to which master port the +/// current request is sent. The `select` can, for example, be driven by an address decoding module +/// to map address ranges to different AXI slaves. +/// +/// ## Design overview +/// +/// ![Block diagram](module.axi_demux.png "Block diagram") +/// +/// Beats on the W channel are routed by demultiplexer according to the selection for the +/// corresponding AW beat. This relies on the AXI property that W bursts must be sent in the same +/// order as AW beats and beats from different W bursts may not be interleaved. +/// +/// Beats on the B and R channel are multiplexed from the master ports to the slave port with +/// a round-robin arbitration tree. module axi_demux #( parameter int unsigned AxiIdWidth = 32'd0, + parameter bit AtopSupport = 1'b1, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, parameter type ar_chan_t = logic, parameter type r_chan_t = logic, - parameter type req_t = logic, - parameter type resp_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, parameter int unsigned NoMstPorts = 32'd0, parameter int unsigned MaxTrans = 32'd8, parameter int unsigned AxiLookBits = 32'd3, parameter bit UniqueIds = 1'b0, - parameter bit FallThrough = 1'b0, parameter bit SpillAw = 1'b1, parameter bit SpillW = 1'b0, parameter bit SpillB = 1'b0, @@ -39,651 +62,152 @@ module axi_demux #( parameter int unsigned SelectWidth = (NoMstPorts > 32'd1) ? $clog2(NoMstPorts) : 32'd1, parameter type select_t = logic [SelectWidth-1:0] ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, + input logic clk_i, + input logic rst_ni, + input logic test_i, // Slave Port - input req_t slv_req_i, - input select_t slv_aw_select_i, - input select_t slv_ar_select_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + input select_t slv_aw_select_i, + input select_t slv_ar_select_i, + output axi_resp_t slv_resp_o, // Master Ports - output req_t [NoMstPorts-1:0] mst_reqs_o, - input resp_t [NoMstPorts-1:0] mst_resps_i + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); - localparam int unsigned IdCounterWidth = MaxTrans > 1 ? $clog2(MaxTrans) : 1; - - //-------------------------------------- - // Typedefs for the FIFOs / Queues - //-------------------------------------- - typedef struct packed { - aw_chan_t aw_chan; - select_t aw_select; - } aw_chan_select_t; - typedef struct packed { - ar_chan_t ar_chan; - select_t ar_select; - } ar_chan_select_t; - - // pass through if only one master port - if (NoMstPorts == 32'h1) begin : gen_no_demux - assign mst_reqs_o[0] = slv_req_i; - assign slv_resp_o = mst_resps_i; - // other non degenerate cases - end else begin : gen_demux - - //-------------------------------------- - //-------------------------------------- - // Signal Declarations - //-------------------------------------- - //-------------------------------------- - - //-------------------------------------- - // Write Transaction - //-------------------------------------- - // comes from spill register at input - aw_chan_select_t slv_aw_chan_select; - logic slv_aw_valid, slv_aw_ready; - - // AW ID counter - select_t lookup_aw_select; - logic aw_select_occupied, aw_id_cnt_full; - logic aw_push; - // Upon an ATOP load, inject IDs from the AW into the AR channel - logic atop_inject; - - // W FIFO: stores the decision to which master W beats should go - logic w_fifo_pop; - logic w_fifo_full, w_fifo_empty; - select_t w_select; - - // Register which locks the AW valid signal - logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; - logic aw_valid, aw_ready; - - // W channel from spill reg - w_chan_t slv_w_chan; - logic slv_w_valid, slv_w_ready; - - // B channles input into the arbitration - b_chan_t [NoMstPorts-1:0] mst_b_chans; - logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies; - - // B channel to spill register - b_chan_t slv_b_chan; - logic slv_b_valid, slv_b_ready; - - //-------------------------------------- - // Read Transaction - //-------------------------------------- - // comes from spill register at input - ar_chan_select_t slv_ar_chan_select; - logic slv_ar_valid, slv_ar_ready; - - // AR ID counter - select_t lookup_ar_select; - logic ar_select_occupied, ar_id_cnt_full; - logic ar_push; - - // Register which locks the AR valid signel - logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock; - logic ar_valid, ar_ready; - - // R channles input into the arbitration - r_chan_t [NoMstPorts-1:0] mst_r_chans; - logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies; - - // R channel to spill register - r_chan_t slv_r_chan; - logic slv_r_valid, slv_r_ready; - - //-------------------------------------- - //-------------------------------------- - // Channel Control - //-------------------------------------- - //-------------------------------------- - - //-------------------------------------- - // AW Channel - //-------------------------------------- - // spill register at the channel input - // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before - // instantiating `spill_register`. - typedef logic [$bits(aw_chan_select_t)-1:0] aw_chan_select_flat_t; - aw_chan_select_flat_t slv_aw_chan_select_in_flat, - slv_aw_chan_select_out_flat; - assign slv_aw_chan_select_in_flat = {slv_req_i.aw, slv_aw_select_i}; - spill_register #( - .T ( aw_chan_select_flat_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) i_aw_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_resp_o.aw_ready ), - .data_i ( slv_aw_chan_select_in_flat ), - .valid_o ( slv_aw_valid ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan_select_out_flat ) - ); - assign slv_aw_chan_select = slv_aw_chan_select_out_flat; - - // Control of the AW handshake - always_comb begin - // AXI Handshakes - slv_aw_ready = 1'b0; - aw_valid = 1'b0; - // `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert - // a valid if there was no corresponding ready. As this process has to be able to inject - // an AXI ID into the counter of the AR channel on an ATOP, there could be a case where - // this process waits on `aw_ready` but in the mean time on the AR channel the counter gets - // full. - lock_aw_valid_d = lock_aw_valid_q; - load_aw_lock = 1'b0; - // AW ID counter and W FIFO - aw_push = 1'b0; - // ATOP injection into ar counter - atop_inject = 1'b0; - // we had an arbitration decision, the valid is locked, wait for the transaction - if (lock_aw_valid_q) begin - aw_valid = 1'b1; - // transaction - if (aw_ready) begin - slv_aw_ready = 1'b1; - lock_aw_valid_d = 1'b0; - load_aw_lock = 1'b1; - atop_inject = slv_aw_chan_select.aw_chan.atop[5]; // inject the ATOP if necessary - end - end else begin - // Process can start handling a transaction if its `i_aw_id_counter` and `w_fifo` have - // space in them. Further check if we could inject something on the AR channel. - if (!aw_id_cnt_full && !w_fifo_full && !ar_id_cnt_full) begin - // there is a valid AW vector make the id lookup and go further, if it passes - if (slv_aw_valid && (!aw_select_occupied || - (slv_aw_chan_select.aw_select == lookup_aw_select))) begin - // connect the handshake - aw_valid = 1'b1; - // push arbitration to the W FIFO regardless, do not wait for the AW transaction - aw_push = 1'b1; - // on AW transaction - if (aw_ready) begin - slv_aw_ready = 1'b1; - atop_inject = slv_aw_chan_select.aw_chan.atop[5]; - // no AW transaction this cycle, lock the decision - end else begin - lock_aw_valid_d = 1'b1; - load_aw_lock = 1'b1; - end - end - end - end - end - - // lock the valid signal, as the selection gets pushed into the W FIFO on first assertion, - // prevent further pushing - `FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni) - - if (UniqueIds) begin : gen_unique_ids_aw - // If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among - // all in-flight write transactions, or all write transactions with a given ID target the same - // master port as all write transactions with the same ID, or both. This means that the - // signals that are driven by the ID counters if this parameter is not set can instead be - // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_aw_select = slv_aw_chan_select.aw_select; - assign aw_select_occupied = 1'b0; - assign aw_id_cnt_full = 1'b0; - end else begin : gen_aw_id_counter - axi_demux_id_counters #( - .AxiIdBits ( AxiLookBits ), - .CounterWidth ( IdCounterWidth ), - .mst_port_select_t ( select_t ) - ) i_aw_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_aw_select ), - .lookup_mst_select_occupied_o ( aw_select_occupied ), - .full_o ( aw_id_cnt_full ), - .inject_axi_id_i ( '0 ), - .inject_i ( 1'b0 ), - .push_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_aw_chan_select.aw_select ), - .push_i ( aw_push ), - .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_b_valid & slv_b_ready ) - ); - // pop from ID counter on outward transaction - end - - // FIFO to save W selection - fifo_v3 #( - .FALL_THROUGH ( FallThrough ), - .DEPTH ( MaxTrans ), - .dtype ( select_t ) - ) i_w_fifo ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i ( 1'b0 ), - .testmode_i( test_i ), - .full_o ( w_fifo_full ), - .empty_o ( w_fifo_empty ), - .usage_o ( ), - .data_i ( slv_aw_chan_select.aw_select ), - .push_i ( aw_push ), // controlled from proc_aw_chan - .data_o ( w_select ), // where the w beat should go - .pop_i ( w_fifo_pop ) // controlled from proc_w_chan - ); - - //-------------------------------------- - // W Channel - //-------------------------------------- - spill_register #( - .T ( w_chan_t ), - .Bypass ( ~SpillW ) - ) i_w_spill_reg( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.w_valid ), - .ready_o ( slv_resp_o.w_ready ), - .data_i ( slv_req_i.w ), - .valid_o ( slv_w_valid ), - .ready_i ( slv_w_ready ), - .data_o ( slv_w_chan ) - ); - - //-------------------------------------- - // B Channel - //-------------------------------------- - // optional spill register - spill_register #( - .T ( b_chan_t ), - .Bypass ( ~SpillB ) - ) i_b_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_b_valid ), - .ready_o ( slv_b_ready ), - .data_i ( slv_b_chan ), - .valid_o ( slv_resp_o.b_valid ), - .ready_i ( slv_req_i.b_ready ), - .data_o ( slv_resp_o.b ) - ); - - // Arbitration of the different B responses - rr_arb_tree #( - .NumIn ( NoMstPorts ), - .DataType ( b_chan_t ), - .AxiVldRdy( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_b_mux ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i( 1'b0 ), - .rr_i ( '0 ), - .req_i ( mst_b_valids ), - .gnt_o ( mst_b_readies ), - .data_i ( mst_b_chans ), - .gnt_i ( slv_b_ready ), - .req_o ( slv_b_valid ), - .data_o ( slv_b_chan ), - .idx_o ( ) - ); - - //-------------------------------------- - // AR Channel - //-------------------------------------- - // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before - // instantiating `spill_register`. - typedef logic [$bits(ar_chan_select_t)-1:0] ar_chan_select_flat_t; - ar_chan_select_flat_t slv_ar_chan_select_in_flat, - slv_ar_chan_select_out_flat; - assign slv_ar_chan_select_in_flat = {slv_req_i.ar, slv_ar_select_i}; - spill_register #( - .T ( ar_chan_select_flat_t ), - .Bypass ( ~SpillAr ) - ) i_ar_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_resp_o.ar_ready ), - .data_i ( slv_ar_chan_select_in_flat ), - .valid_o ( slv_ar_valid ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan_select_out_flat ) - ); - assign slv_ar_chan_select = slv_ar_chan_select_out_flat; - - // control of the AR handshake - always_comb begin - // AXI Handshakes - slv_ar_ready = 1'b0; - ar_valid = 1'b0; - // `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid` - // if there was no corresponding `ar_ready`. There is the possibility that an injection - // of a R response from an `atop` from the AW channel can change the occupied flag of the - // `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion. - lock_ar_valid_d = lock_ar_valid_q; - load_ar_lock = 1'b0; - // AR id counter - ar_push = 1'b0; - // The process had an arbitration decision in a previous cycle, the valid is locked, - // wait for the AR transaction. - if (lock_ar_valid_q) begin - ar_valid = 1'b1; - // transaction - if (ar_ready) begin - slv_ar_ready = 1'b1; - ar_push = 1'b1; - lock_ar_valid_d = 1'b0; - load_ar_lock = 1'b1; - end - end else begin - // The process can start handling AR transaction if `i_ar_id_counter` has space. - if (!ar_id_cnt_full) begin - // There is a valid AR, so look the ID up. - if (slv_ar_valid && (!ar_select_occupied || - (slv_ar_chan_select.ar_select == lookup_ar_select))) begin - // connect the AR handshake - ar_valid = 1'b1; - // on transaction - if (ar_ready) begin - slv_ar_ready = 1'b1; - ar_push = 1'b1; - // no transaction this cycle, lock the valid decision! - end else begin - lock_ar_valid_d = 1'b1; - load_ar_lock = 1'b1; - end - end - end - end - end - - // this ff is needed so that ar does not get de-asserted if an atop gets injected - `FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni) - - if (UniqueIds) begin : gen_unique_ids_ar - // If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among - // all in-flight read transactions, or all read transactions with a given ID target the same - // master port as all read transactions with the same ID, or both. This means that the - // signals that are driven by the ID counters if this parameter is not set can instead be - // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_ar_select = slv_ar_chan_select.ar_select; - assign ar_select_occupied = 1'b0; - assign ar_id_cnt_full = 1'b0; - end else begin : gen_ar_id_counter - axi_demux_id_counters #( - .AxiIdBits ( AxiLookBits ), - .CounterWidth ( IdCounterWidth ), - .mst_port_select_t ( select_t ) - ) i_ar_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_ar_chan_select.ar_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_ar_select ), - .lookup_mst_select_occupied_o ( ar_select_occupied ), - .full_o ( ar_id_cnt_full ), - .inject_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), - .inject_i ( atop_inject ), - .push_axi_id_i ( slv_ar_chan_select.ar_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_ar_chan_select.ar_select ), - .push_i ( ar_push ), - .pop_axi_id_i ( slv_r_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_r_valid & slv_r_ready & slv_r_chan.last ) - ); - end - - //-------------------------------------- - // R Channel - //-------------------------------------- - // optional spill register - spill_register #( - .T ( r_chan_t ), - .Bypass ( ~SpillR ) - ) i_r_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_r_valid ), - .ready_o ( slv_r_ready ), - .data_i ( slv_r_chan ), - .valid_o ( slv_resp_o.r_valid ), - .ready_i ( slv_req_i.r_ready ), - .data_o ( slv_resp_o.r ) - ); - - // Arbitration of the different r responses - rr_arb_tree #( - .NumIn ( NoMstPorts ), - .DataType ( r_chan_t ), - .AxiVldRdy( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_r_mux ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i( 1'b0 ), - .rr_i ( '0 ), - .req_i ( mst_r_valids ), - .gnt_o ( mst_r_readies ), - .data_i ( mst_r_chans ), - .gnt_i ( slv_r_ready ), - .req_o ( slv_r_valid ), - .data_o ( slv_r_chan ), - .idx_o ( ) - ); - - assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready; - assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready; - - // process that defines the individual demuxes and assignments for the arbitration - // as mst_reqs_o has to be drivem from the same always comb block! - always_comb begin - // default assignments - mst_reqs_o = '0; - slv_w_ready = 1'b0; - w_fifo_pop = 1'b0; - - for (int unsigned i = 0; i < NoMstPorts; i++) begin - // AW channel - mst_reqs_o[i].aw = slv_aw_chan_select.aw_chan; - mst_reqs_o[i].aw_valid = 1'b0; - if (aw_valid && (slv_aw_chan_select.aw_select == i)) begin - mst_reqs_o[i].aw_valid = 1'b1; - end - - // W channel - mst_reqs_o[i].w = slv_w_chan; - mst_reqs_o[i].w_valid = 1'b0; - if (!w_fifo_empty && (w_select == i)) begin - mst_reqs_o[i].w_valid = slv_w_valid; - slv_w_ready = mst_resps_i[i].w_ready; - w_fifo_pop = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; - end - - // B channel - mst_reqs_o[i].b_ready = mst_b_readies[i]; - - // AR channel - mst_reqs_o[i].ar = slv_ar_chan_select.ar_chan; - mst_reqs_o[i].ar_valid = 1'b0; - if (ar_valid && (slv_ar_chan_select.ar_select == i)) begin - mst_reqs_o[i].ar_valid = 1'b1; - end - - // R channel - mst_reqs_o[i].r_ready = mst_r_readies[i]; - end - end - // unpack the response B and R channels for the arbitration - for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels - assign mst_b_chans[i] = mst_resps_i[i].b; - assign mst_b_valids[i] = mst_resps_i[i].b_valid; - assign mst_r_chans[i] = mst_resps_i[i].r; - assign mst_r_valids[i] = mst_resps_i[i].r_valid; - end - - -// Validate parameters. -// pragma translate_off -`ifndef VERILATOR -`ifndef XSIM - initial begin: validate_params - no_mst_ports: assume (NoMstPorts > 0) else - $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); - AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else - $fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth."); - end - default disable iff (!rst_ni); - aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> - (slv_aw_select_i < NoMstPorts))) else - $fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\ - NoMstPorts: %d", slv_aw_select_i, NoMstPorts); - ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |-> - (slv_ar_select_i < NoMstPorts))) else - $fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\ - NoMstPorts: %d", slv_ar_select_i, NoMstPorts); - aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else - $fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle."); - ar_valid_stable: assert property( @(posedge clk_i) - (ar_valid && !ar_ready) |=> ar_valid) else - $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); - aw_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_chan_select)) else - $fatal(1, "slv_aw_chan_select unstable with valid set."); - ar_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_chan_select)) else - $fatal(1, "slv_aw_chan_select unstable with valid set."); - internal_ar_select: assert property( @(posedge clk_i) - (ar_valid |-> slv_ar_chan_select.ar_select < NoMstPorts)) - else $fatal(1, "slv_ar_chan_select.ar_select illegal while ar_valid."); - internal_aw_select: assert property( @(posedge clk_i) - (aw_valid |-> slv_aw_chan_select.aw_select < NoMstPorts)) - else $fatal(1, "slv_aw_chan_select.aw_select illegal while aw_valid."); -`endif -`endif -// pragma translate_on - end -endmodule - -module axi_demux_id_counters #( - // the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters - parameter int unsigned AxiIdBits = 2, - parameter int unsigned CounterWidth = 4, - parameter type mst_port_select_t = logic -) ( - input clk_i, // Clock - input rst_ni, // Asynchronous reset active low - // lookup - input logic [AxiIdBits-1:0] lookup_axi_id_i, - output mst_port_select_t lookup_mst_select_o, - output logic lookup_mst_select_occupied_o, - // push - output logic full_o, - input logic [AxiIdBits-1:0] push_axi_id_i, - input mst_port_select_t push_mst_select_i, - input logic push_i, - // inject ATOPs in AR channel - input logic [AxiIdBits-1:0] inject_axi_id_i, - input logic inject_i, - // pop - input logic [AxiIdBits-1:0] pop_axi_id_i, - input logic pop_i -); - localparam int unsigned NoCounters = 2**AxiIdBits; - typedef logic [CounterWidth-1:0] cnt_t; - - // registers, each gets loaded when push_en[i] - mst_port_select_t [NoCounters-1:0] mst_select_q; - - // counter signals - logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full; + axi_req_t slv_req_cut; + axi_resp_t slv_resp_cut; + + logic slv_aw_ready_chan, slv_aw_ready_sel; + logic slv_aw_valid_chan, slv_aw_valid_sel; + + logic slv_ar_ready_chan, slv_ar_ready_sel; + logic slv_ar_valid_chan, slv_ar_valid_sel; + + select_t slv_aw_select, slv_ar_select; + + spill_register #( + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_chan ), + .data_i ( slv_req_i.aw ), + .valid_o ( slv_aw_valid_chan ), + .ready_i ( slv_resp_cut.aw_ready ), + .data_o ( slv_req_cut.aw ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAw ) + ) i_aw_select_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_sel ), + .data_i ( slv_aw_select_i ), + .valid_o ( slv_aw_valid_sel ), + .ready_i ( slv_resp_cut.aw_ready ), + .data_o ( slv_aw_select ) + ); - //----------------------------------- - // Lookup - //----------------------------------- - assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i]; - assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i]; - //----------------------------------- - // Push and Pop - //----------------------------------- - assign push_en = (push_i) ? (1 << push_axi_id_i) : '0; - assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0; - assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0; - assign full_o = |cnt_full; - // counters - for (genvar i = 0; i < NoCounters; i++) begin : gen_counters - logic cnt_en, cnt_down, overflow; - cnt_t cnt_delta, in_flight; - always_comb begin - unique case ({push_en[i], inject_en[i], pop_en[i]}) - 3'b001 : begin // pop_i = -1 - cnt_en = 1'b1; - cnt_down = 1'b1; - cnt_delta = cnt_t'(1); - end - 3'b010 : begin // inject_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - // 3'b011, inject_i & pop_i = 0 --> use default - 3'b100 : begin // push_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - // 3'b101, push_i & pop_i = 0 --> use default - 3'b110 : begin // push_i & inject_i = +2 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(2); - end - 3'b111 : begin // push_i & inject_i & pop_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - default : begin // do nothing to the counters - cnt_en = 1'b0; - cnt_down = 1'b0; - cnt_delta = cnt_t'(0); - end - endcase - end + assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; + assign slv_req_cut.aw_valid = slv_aw_valid_chan & slv_aw_valid_sel; + + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_resp_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( slv_req_cut.w_valid ), + .ready_i ( slv_resp_cut.w_ready ), + .data_o ( slv_req_cut.w ) + ); + spill_register #( + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_chan ), + .data_i ( slv_req_i.ar ), + .valid_o ( slv_ar_valid_chan ), + .ready_i ( slv_resp_cut.ar_ready ), + .data_o ( slv_req_cut.ar ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAr ) + ) i_ar_sel_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_sel ), + .data_i ( slv_ar_select_i ), + .valid_o ( slv_ar_valid_sel ), + .ready_i ( slv_resp_cut.ar_ready ), + .data_o ( slv_ar_select ) + ); - delta_counter #( - .WIDTH ( CounterWidth ), - .STICKY_OVERFLOW ( 1'b0 ) - ) i_in_flight_cnt ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .clear_i ( 1'b0 ), - .en_i ( cnt_en ), - .load_i ( 1'b0 ), - .down_i ( cnt_down ), - .delta_i ( cnt_delta ), - .d_i ( '0 ), - .q_o ( in_flight ), - .overflow_o ( overflow ) - ); - assign occupied[i] = |in_flight; - assign cnt_full[i] = overflow | (&in_flight); + assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; + assign slv_req_cut.ar_valid = slv_ar_valid_chan & slv_ar_valid_sel; + + spill_register #( + .T ( b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_resp_cut.b_valid ), + .ready_o ( slv_req_cut.b_ready ), + .data_i ( slv_resp_cut.b ), + .valid_o ( slv_resp_o.b_valid ), + .ready_i ( slv_req_i.b_ready ), + .data_o ( slv_resp_o.b ) + ); + spill_register #( + .T ( r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_resp_cut.r_valid ), + .ready_o ( slv_req_cut.r_ready ), + .data_i ( slv_resp_cut.r ), + .valid_o ( slv_resp_o.r_valid ), + .ready_i ( slv_req_i.r_ready ), + .data_o ( slv_resp_o.r ) + ); - // holds the selection signal for this id - `FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni) + axi_demux_simple #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport( AtopSupport ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( NoMstPorts ), + .MaxTrans ( MaxTrans ), + .AxiLookBits( AxiLookBits ), + .UniqueIds ( UniqueIds ) + ) i_demux_simple ( + .clk_i, + .rst_ni, + .test_i, + + .slv_req_i ( slv_req_cut ), + .slv_aw_select_i ( slv_aw_select ), + .slv_ar_select_i ( slv_ar_select ), + .slv_resp_o ( slv_resp_cut ), + .mst_reqs_o ( mst_reqs_o ), + .mst_resps_i ( mst_resps_i ) + ); -// pragma translate_off -`ifndef VERILATOR -`ifndef XSIM - // Validate parameters. - cnt_underflow: assert property( - @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else - $fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\ - The reason is probably a faulty AXI response.", i); -`endif -`endif -// pragma translate_on - end endmodule // interface wrapper @@ -691,6 +215,7 @@ endmodule `include "axi/typedef.svh" module axi_demux_intf #( parameter int unsigned AXI_ID_WIDTH = 32'd0, // Synopsys DC requires default value for params + parameter bit ATOP_SUPPORT = 1'b1, parameter int unsigned AXI_ADDR_WIDTH = 32'd0, parameter int unsigned AXI_DATA_WIDTH = 32'd0, parameter int unsigned AXI_USER_WIDTH = 32'd0, @@ -698,7 +223,6 @@ module axi_demux_intf #( parameter int unsigned MAX_TRANS = 32'd8, parameter int unsigned AXI_LOOK_BITS = 32'd3, parameter bit UNIQUE_IDS = 1'b0, - parameter bit FALL_THROUGH = 1'b0, parameter bit SPILL_AW = 1'b1, parameter bit SPILL_W = 1'b0, parameter bit SPILL_B = 1'b0, @@ -727,13 +251,13 @@ module axi_demux_intf #( `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req; - resp_t slv_resp; - req_t [NO_MST_PORTS-1:0] mst_req; - resp_t [NO_MST_PORTS-1:0] mst_resp; + axi_req_t slv_req; + axi_resp_t slv_resp; + axi_req_t [NO_MST_PORTS-1:0] mst_req; + axi_resp_t [NO_MST_PORTS-1:0] mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -745,18 +269,18 @@ module axi_demux_intf #( axi_demux #( .AxiIdWidth ( AXI_ID_WIDTH ), // ID Width - .aw_chan_t ( aw_chan_t ), // AW Channel Type - .w_chan_t ( w_chan_t ), // W Channel Type - .b_chan_t ( b_chan_t ), // B Channel Type - .ar_chan_t ( ar_chan_t ), // AR Channel Type - .r_chan_t ( r_chan_t ), // R Channel Type - .req_t ( req_t ), - .resp_t ( resp_t ), + .AtopSupport ( ATOP_SUPPORT ), + .aw_chan_t ( aw_chan_t ), // AW Channel Type + .w_chan_t ( w_chan_t ), // W Channel Type + .b_chan_t ( b_chan_t ), // B Channel Type + .ar_chan_t ( ar_chan_t ), // AR Channel Type + .r_chan_t ( r_chan_t ), // R Channel Type + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoMstPorts ( NO_MST_PORTS ), .MaxTrans ( MAX_TRANS ), .AxiLookBits ( AXI_LOOK_BITS ), .UniqueIds ( UNIQUE_IDS ), - .FallThrough ( FALL_THROUGH ), .SpillAw ( SPILL_AW ), .SpillW ( SPILL_W ), .SpillB ( SPILL_B ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_demux_simple.sv b/hw/vendor/pulp_platform_axi/src/axi_demux_simple.sv new file mode 100644 index 000000000..1c11f794a --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_demux_simple.sv @@ -0,0 +1,633 @@ +// Copyright (c) 2019 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Wolfgang Roenninger +// - Michael Rogenmoser +// - Thomas Benz +// - Andreas Kurth + +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" +`include "axi/assign.svh" + +`ifdef QUESTA +// Derive `TARGET_VSIM`, which is used for tool-specific workarounds in this file, from `QUESTA`, +// which is automatically set in Questa. +`define TARGET_VSIM +`endif + +/// Demultiplex one AXI4+ATOP slave port to multiple AXI4+ATOP master ports. +/// +/// The AW and AR slave channels each have a `select` input to determine to which master port the +/// current request is sent. The `select` can, for example, be driven by an address decoding module +/// to map address ranges to different AXI slaves. +/// +/// ## Design overview +/// +/// ![Block diagram](module.axi_demux.png "Block diagram") +/// +/// Beats on the W channel are routed by demultiplexer according to the selection for the +/// corresponding AW beat. This relies on the AXI property that W bursts must be sent in the same +/// order as AW beats and beats from different W bursts may not be interleaved. +/// +/// Beats on the B and R channel are multiplexed from the master ports to the slave port with +/// a round-robin arbitration tree. +module axi_demux_simple #( + parameter int unsigned AxiIdWidth = 32'd0, + parameter bit AtopSupport = 1'b1, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, + parameter int unsigned NoMstPorts = 32'd0, + parameter int unsigned MaxTrans = 32'd8, + parameter int unsigned AxiLookBits = 32'd3, + parameter bit UniqueIds = 1'b0, + // Dependent parameters, DO NOT OVERRIDE! + parameter int unsigned SelectWidth = (NoMstPorts > 32'd1) ? $clog2(NoMstPorts) : 32'd1, + parameter type select_t = logic [SelectWidth-1:0] +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + // Slave Port + input axi_req_t slv_req_i, + input select_t slv_aw_select_i, + input select_t slv_ar_select_i, + output axi_resp_t slv_resp_o, + // Master Ports + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i +); + + localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); + typedef logic [IdCounterWidth-1:0] id_cnt_t; + + // pass through if only one master port + if (NoMstPorts == 32'h1) begin : gen_no_demux + `AXI_ASSIGN_REQ_STRUCT(mst_reqs_o[0], slv_req_i) + `AXI_ASSIGN_RESP_STRUCT(slv_resp_o, mst_resps_i[0]) + end else begin + + //-------------------------------------- + //-------------------------------------- + // Signal Declarations + //-------------------------------------- + //-------------------------------------- + + //-------------------------------------- + // Write Transaction + //-------------------------------------- + + // Register which locks the AW valid signal + logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; + logic aw_valid, aw_ready; + + // AW ID counter + select_t lookup_aw_select; + logic aw_select_occupied, aw_id_cnt_full; + // Upon an ATOP load, inject IDs from the AW into the AR channel + logic atop_inject; + + // W select counter: stores the decision to which master W beats should go + select_t w_select, w_select_q; + logic w_select_valid; + id_cnt_t w_open; + logic w_cnt_up, w_cnt_down; + + // B channles input into the arbitration + logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies; + + //-------------------------------------- + // Read Transaction + //-------------------------------------- + + // AR ID counter + select_t lookup_ar_select; + logic ar_select_occupied, ar_id_cnt_full; + logic ar_push; + + // Register which locks the AR valid signel + logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock; + logic ar_valid, ar_ready; + + logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies; + + + + + + + + //-------------------------------------- + // Channel Control + //-------------------------------------- + //-------------------------------------- + + //-------------------------------------- + // AW Channel + //-------------------------------------- + + // Control of the AW handshake + always_comb begin + // AXI Handshakes + slv_resp_o.aw_ready = 1'b0; + aw_valid = 1'b0; + // `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert + // a valid if there was no corresponding ready. As this process has to be able to inject + // an AXI ID into the counter of the AR channel on an ATOP, there could be a case where + // this process waits on `aw_ready` but in the mean time on the AR channel the counter gets + // full. + lock_aw_valid_d = lock_aw_valid_q; + load_aw_lock = 1'b0; + // AW ID counter and W FIFO + w_cnt_up = 1'b0; + // ATOP injection into ar counter + atop_inject = 1'b0; + // we had an arbitration decision, the valid is locked, wait for the transaction + if (lock_aw_valid_q) begin + aw_valid = 1'b1; + // transaction + if (aw_ready) begin + slv_resp_o.aw_ready = 1'b1; + lock_aw_valid_d = 1'b0; + load_aw_lock = 1'b1; + // inject the ATOP if necessary + atop_inject = slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + end + end else begin + // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that + // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this + // only applies if ATOPs are supported at all). + if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && + (!(ar_id_cnt_full && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) || + !AtopSupport)) begin + // There is a valid AW vector make the id lookup and go further, if it passes. + // Also stall if previous transmitted AWs still have active W's in flight. + // This prevents deadlocking of the W channel. The counters are there for the + // Handling of the B responses. + if (slv_req_i.aw_valid && + ((w_open == '0) || (w_select == slv_aw_select_i)) && + (!aw_select_occupied || (slv_aw_select_i == lookup_aw_select))) begin + // connect the handshake + aw_valid = 1'b1; + // push arbitration to the W FIFO regardless, do not wait for the AW transaction + w_cnt_up = 1'b1; + // on AW transaction + if (aw_ready) begin + slv_resp_o.aw_ready = 1'b1; + atop_inject = slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + // no AW transaction this cycle, lock the decision + end else begin + lock_aw_valid_d = 1'b1; + load_aw_lock = 1'b1; + end + end + end + end + end + + // lock the valid signal, as the selection gets pushed into the W FIFO on first assertion, + // prevent further pushing + `FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni) + + if (UniqueIds) begin : gen_unique_ids_aw + // If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among + // all in-flight write transactions, or all write transactions with a given ID target the same + // master port as all write transactions with the same ID, or both. This means that the + // signals that are driven by the ID counters if this parameter is not set can instead be + // derived from existing signals. The ID counters can therefore be omitted. + assign lookup_aw_select = slv_aw_select_i; + assign aw_select_occupied = 1'b0; + assign aw_id_cnt_full = 1'b0; + end else begin : gen_aw_id_counter + axi_demux_id_counters #( + .AxiIdBits ( AxiLookBits ), + .CounterWidth ( IdCounterWidth ), + .mst_port_select_t ( select_t ) + ) i_aw_id_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_aw_select ), + .lookup_mst_select_occupied_o ( aw_select_occupied ), + .full_o ( aw_id_cnt_full ), + .inject_axi_id_i ( '0 ), + .inject_i ( 1'b0 ), + .push_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_aw_select_i ), + .push_i ( w_cnt_up ), + .pop_axi_id_i ( slv_resp_o.b.id[0+:AxiLookBits] ), + .pop_i ( slv_resp_o.b_valid & slv_req_i.b_ready ) + ); + // pop from ID counter on outward transaction + end + + // This counter steers the demultiplexer of the W channel. + // `w_select` determines, which handshaking is connected. + // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as + // `slv_aw_select_i`. + counter #( + .WIDTH ( IdCounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_counter_open_w ( + .clk_i, + .rst_ni, + .clear_i ( 1'b0 ), + .en_i ( w_cnt_up ^ w_cnt_down ), + .load_i ( 1'b0 ), + .down_i ( w_cnt_down ), + .d_i ( '0 ), + .q_o ( w_open ), + .overflow_o ( /*not used*/ ) + ); + + `FFLARN(w_select_q, slv_aw_select_i, w_cnt_up, select_t'(0), clk_i, rst_ni) + assign w_select = (|w_open) ? w_select_q : slv_aw_select_i; + assign w_select_valid = w_cnt_up | (|w_open); + + //-------------------------------------- + // W Channel + //-------------------------------------- + + //-------------------------------------- + // B Channel + //-------------------------------------- + logic [cf_math_pkg::idx_width(NoMstPorts)-1:0] b_idx; + + // Arbitration of the different B responses + rr_arb_tree #( + .NumIn ( NoMstPorts ), + .DataType ( logic ), + .AxiVldRdy( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_b_mux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i( 1'b0 ), + .rr_i ( '0 ), + .req_i ( mst_b_valids ), + .gnt_o ( mst_b_readies ), + .data_i ( '0 ), + .gnt_i ( slv_req_i.b_ready ), + .req_o ( slv_resp_o.b_valid ), + .data_o ( ), + .idx_o ( b_idx ) + ); + + always_comb begin + if (slv_resp_o.b_valid) begin + `AXI_SET_B_STRUCT(slv_resp_o.b, mst_resps_i[b_idx].b) + end else begin + slv_resp_o.b = '0; + end + end + + //-------------------------------------- + // AR Channel + //-------------------------------------- + + // control of the AR handshake + always_comb begin + // AXI Handshakes + slv_resp_o.ar_ready = 1'b0; + ar_valid = 1'b0; + // `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid` + // if there was no corresponding `ar_ready`. There is the possibility that an injection + // of a R response from an `atop` from the AW channel can change the occupied flag of the + // `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion. + lock_ar_valid_d = lock_ar_valid_q; + load_ar_lock = 1'b0; + // AR id counter + ar_push = 1'b0; + // The process had an arbitration decision in a previous cycle, the valid is locked, + // wait for the AR transaction. + if (lock_ar_valid_q) begin + ar_valid = 1'b1; + // transaction + if (ar_ready) begin + slv_resp_o.ar_ready = 1'b1; + ar_push = 1'b1; + lock_ar_valid_d = 1'b0; + load_ar_lock = 1'b1; + end + end else begin + // The process can start handling AR transaction if `i_ar_id_counter` has space. + if (!ar_id_cnt_full) begin + // There is a valid AR, so look the ID up. + if (slv_req_i.ar_valid && (!ar_select_occupied || + (slv_ar_select_i == lookup_ar_select))) begin + // connect the AR handshake + ar_valid = 1'b1; + // on transaction + if (ar_ready) begin + slv_resp_o.ar_ready = 1'b1; + ar_push = 1'b1; + // no transaction this cycle, lock the valid decision! + end else begin + lock_ar_valid_d = 1'b1; + load_ar_lock = 1'b1; + end + end + end + end + end + + // this ff is needed so that ar does not get de-asserted if an atop gets injected + `FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni) + + if (UniqueIds) begin : gen_unique_ids_ar + // If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among + // all in-flight read transactions, or all read transactions with a given ID target the same + // master port as all read transactions with the same ID, or both. This means that the + // signals that are driven by the ID counters if this parameter is not set can instead be + // derived from existing signals. The ID counters can therefore be omitted. + assign lookup_ar_select = slv_ar_select_i; + assign ar_select_occupied = 1'b0; + assign ar_id_cnt_full = 1'b0; + end else begin : gen_ar_id_counter + axi_demux_id_counters #( + .AxiIdBits ( AxiLookBits ), + .CounterWidth ( IdCounterWidth ), + .mst_port_select_t ( select_t ) + ) i_ar_id_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_req_i.ar.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_ar_select ), + .lookup_mst_select_occupied_o ( ar_select_occupied ), + .full_o ( ar_id_cnt_full ), + .inject_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .inject_i ( atop_inject ), + .push_axi_id_i ( slv_req_i.ar.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_ar_select_i ), + .push_i ( ar_push ), + .pop_axi_id_i ( slv_resp_o.r.id[0+:AxiLookBits] ), + .pop_i ( slv_resp_o.r_valid & slv_req_i.r_ready & slv_resp_o.r.last ) + ); + end + + //-------------------------------------- + // R Channel + //-------------------------------------- + + logic [cf_math_pkg::idx_width(NoMstPorts)-1:0] r_idx; + + // Arbitration of the different r responses + rr_arb_tree #( + .NumIn ( NoMstPorts ), + .DataType ( logic ), + .AxiVldRdy( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_r_mux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i( 1'b0 ), + .rr_i ( '0 ), + .req_i ( mst_r_valids ), + .gnt_o ( mst_r_readies ), + .data_i ( '0 ), + .gnt_i ( slv_req_i.r_ready ), + .req_o ( slv_resp_o.r_valid ), + .data_o (), + .idx_o ( r_idx ) + ); + + always_comb begin + if (slv_resp_o.r_valid) begin + `AXI_SET_R_STRUCT(slv_resp_o.r, mst_resps_i[r_idx].r) + end else begin + slv_resp_o.r = '0; + end + end + + assign ar_ready = ar_valid & mst_resps_i[slv_ar_select_i].ar_ready; + assign aw_ready = aw_valid & mst_resps_i[slv_aw_select_i].aw_ready; + + // process that defines the individual demuxes and assignments for the arbitration + // as mst_reqs_o has to be drivem from the same always comb block! + always_comb begin + // default assignments + mst_reqs_o = '0; + slv_resp_o.w_ready = 1'b0; + w_cnt_down = 1'b0; + + for (int unsigned i = 0; i < NoMstPorts; i++) begin + // AW channel + mst_reqs_o[i].aw = slv_req_i.aw; + mst_reqs_o[i].aw_valid = 1'b0; + if (aw_valid && (slv_aw_select_i == i)) begin + mst_reqs_o[i].aw_valid = 1'b1; + end + + // W channel + mst_reqs_o[i].w = slv_req_i.w; + mst_reqs_o[i].w_valid = 1'b0; + if (w_select_valid && (w_select == i)) begin + mst_reqs_o[i].w_valid = slv_req_i.w_valid; + slv_resp_o.w_ready = mst_resps_i[i].w_ready; + w_cnt_down = slv_req_i.w_valid & mst_resps_i[i].w_ready & slv_req_i.w.last; + end + + // B channel + mst_reqs_o[i].b_ready = mst_b_readies[i]; + + // AR channel + mst_reqs_o[i].ar = slv_req_i.ar; + mst_reqs_o[i].ar_valid = 1'b0; + if (ar_valid && (slv_ar_select_i == i)) begin + mst_reqs_o[i].ar_valid = 1'b1; + end + + // R channel + mst_reqs_o[i].r_ready = mst_r_readies[i]; + end + end + // unpack the response B and R channels for the arbitration + for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels + // assign mst_b_chans[i] = mst_resps_i[i].b; + assign mst_b_valids[i] = mst_resps_i[i].b_valid; + // assign mst_r_chans[i] = mst_resps_i[i].r; + assign mst_r_valids[i] = mst_resps_i[i].r_valid; + end + +// Validate parameters. +// pragma translate_off +`ifndef VERILATOR +`ifndef XSIM + initial begin: validate_params + no_mst_ports: assume (NoMstPorts > 0) else + $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); + AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else + $fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth."); + end + default disable iff (!rst_ni); + aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> + (slv_aw_select_i < NoMstPorts))) else + $fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\ + NoMstPorts: %d", slv_aw_select_i, NoMstPorts); + ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |-> + (slv_ar_select_i < NoMstPorts))) else + $fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\ + NoMstPorts: %d", slv_ar_select_i, NoMstPorts); + aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else + $fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle."); + ar_valid_stable: assert property( @(posedge clk_i) + (ar_valid && !ar_ready) |=> ar_valid) else + $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); + slv_aw_chan_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_req_i.aw)) else + $fatal(1, "slv_aw_chan unstable with valid set."); + slv_aw_select_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_aw_select_i)) else + $fatal(1, "slv_aw_select_i unstable with valid set."); + slv_ar_chan_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_req_i.ar)) else + $fatal(1, "slv_ar_chan unstable with valid set."); + slv_ar_select_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_ar_select_i)) else + $fatal(1, "slv_ar_select_i unstable with valid set."); + internal_ar_select: assert property( @(posedge clk_i) + (ar_valid |-> slv_ar_select_i < NoMstPorts)) + else $fatal(1, "slv_ar_select_i illegal while ar_valid."); + internal_aw_select: assert property( @(posedge clk_i) + (aw_valid |-> slv_aw_select_i < NoMstPorts)) + else $fatal(1, "slv_aw_select_i illegal while aw_valid."); + w_underflow: assert property( @(posedge clk_i) + ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else + $fatal(1, "W counter underflowed!"); + `ASSUME(NoAtopAllowed, !AtopSupport && slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0) +`endif +`endif +// pragma translate_on + end +endmodule + + +module axi_demux_id_counters #( + // the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters + parameter int unsigned AxiIdBits = 2, + parameter int unsigned CounterWidth = 4, + parameter type mst_port_select_t = logic +) ( + input clk_i, // Clock + input rst_ni, // Asynchronous reset active low + // lookup + input logic [AxiIdBits-1:0] lookup_axi_id_i, + output mst_port_select_t lookup_mst_select_o, + output logic lookup_mst_select_occupied_o, + // push + output logic full_o, + input logic [AxiIdBits-1:0] push_axi_id_i, + input mst_port_select_t push_mst_select_i, + input logic push_i, + // inject ATOPs in AR channel + input logic [AxiIdBits-1:0] inject_axi_id_i, + input logic inject_i, + // pop + input logic [AxiIdBits-1:0] pop_axi_id_i, + input logic pop_i +); + localparam int unsigned NoCounters = 2**AxiIdBits; + typedef logic [CounterWidth-1:0] cnt_t; + + // registers, each gets loaded when push_en[i] + mst_port_select_t [NoCounters-1:0] mst_select_q; + + // counter signals + logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full; + + //----------------------------------- + // Lookup + //----------------------------------- + assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i]; + assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i]; + //----------------------------------- + // Push and Pop + //----------------------------------- + assign push_en = (push_i) ? (1 << push_axi_id_i) : '0; + assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0; + assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0; + assign full_o = |cnt_full; + // counters + for (genvar i = 0; i < NoCounters; i++) begin : gen_counters + logic cnt_en, cnt_down, overflow; + cnt_t cnt_delta, in_flight; + always_comb begin + unique case ({push_en[i], inject_en[i], pop_en[i]}) + 3'b001 : begin // pop_i = -1 + cnt_en = 1'b1; + cnt_down = 1'b1; + cnt_delta = cnt_t'(1); + end + 3'b010 : begin // inject_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + // 3'b011, inject_i & pop_i = 0 --> use default + 3'b100 : begin // push_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + // 3'b101, push_i & pop_i = 0 --> use default + 3'b110 : begin // push_i & inject_i = +2 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(2); + end + 3'b111 : begin // push_i & inject_i & pop_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + default : begin // do nothing to the counters + cnt_en = 1'b0; + cnt_down = 1'b0; + cnt_delta = cnt_t'(0); + end + endcase + end + + delta_counter #( + .WIDTH ( CounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_in_flight_cnt ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( 1'b0 ), + .en_i ( cnt_en ), + .load_i ( 1'b0 ), + .down_i ( cnt_down ), + .delta_i ( cnt_delta ), + .d_i ( '0 ), + .q_o ( in_flight ), + .overflow_o ( overflow ) + ); + assign occupied[i] = |in_flight; + assign cnt_full[i] = overflow | (&in_flight); + + // holds the selection signal for this id + `FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni) + +// pragma translate_off +`ifndef VERILATOR +`ifndef XSIM + // Validate parameters. + cnt_underflow: assert property( + @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else + $fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\ + The reason is probably a faulty AXI response.", i); +`endif +`endif +// pragma translate_on + end +endmodule + diff --git a/hw/vendor/pulp_platform_axi/src/axi_dumper.sv b/hw/vendor/pulp_platform_axi/src/axi_dumper.sv new file mode 100644 index 000000000..c3031ea5c --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_dumper.sv @@ -0,0 +1,215 @@ +// Copyright (c) 2019 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Michael Rogenmoser + +/// Simulation-Only dumper for AXI transactions +/// +/// This module writes all handshaked AXI beats to a log file. To use in simulation, +/// ensure `TARGET_SIMULATION` is defined. +module axi_dumper #( + parameter BusName = "axi_bus", + parameter bit LogAW = 1'b1, + parameter bit LogAR = 1'b1, + parameter bit LogW = 1'b0, + parameter bit LogB = 1'b0, + parameter bit LogR = 1'b0, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input axi_req_t axi_req_i, + input axi_resp_t axi_resp_i +); + +`ifdef TARGET_SIMULATION + string fn; + integer f; + initial begin + #1; + $sformat(fn, "axi_trace_%s.log", BusName); + f = $fopen(fn, "w"); + $display("[Tracer] Logging axi accesses to %s", fn); + end + + always_ff @(posedge clk_i) begin : proc_tracer + automatic string aw_data [string]; + automatic string ar_data [string]; + automatic string w_data [string]; + automatic string b_data [string]; + automatic string r_data [string]; + + automatic string aw_string; + automatic string ar_string; + automatic string w_string; + automatic string b_string; + automatic string r_string; + + if (rst_ni) begin + aw_data = '{ + "type" : "\"AW\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_req_i.aw.id), + "addr" : $sformatf("0x%0x", axi_req_i.aw.addr), + "len" : $sformatf("0x%0x", axi_req_i.aw.len), + "size" : $sformatf("0x%0x", axi_req_i.aw.size), + "burst" : $sformatf("0x%0x", axi_req_i.aw.burst), + "lock" : $sformatf("0x%0x", axi_req_i.aw.lock), + "cache" : $sformatf("0x%0x", axi_req_i.aw.cache), + "prot" : $sformatf("0x%0x", axi_req_i.aw.prot), + "qos" : $sformatf("0x%0x", axi_req_i.aw.qos), + "region" : $sformatf("0x%0x", axi_req_i.aw.region), + "atop" : $sformatf("0x%0x", axi_req_i.aw.atop), + "user" : $sformatf("0x%0x", axi_req_i.aw.user) + }; + ar_data = '{ + "type" : "\"AR\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_req_i.ar.id), + "addr" : $sformatf("0x%0x", axi_req_i.ar.addr), + "len" : $sformatf("0x%0x", axi_req_i.ar.len), + "size" : $sformatf("0x%0x", axi_req_i.ar.size), + "burst" : $sformatf("0x%0x", axi_req_i.ar.burst), + "lock" : $sformatf("0x%0x", axi_req_i.ar.lock), + "cache" : $sformatf("0x%0x", axi_req_i.ar.cache), + "prot" : $sformatf("0x%0x", axi_req_i.ar.prot), + "qos" : $sformatf("0x%0x", axi_req_i.ar.qos), + "region" : $sformatf("0x%0x", axi_req_i.ar.region), + "user" : $sformatf("0x%0x", axi_req_i.ar.user) + }; + w_data = '{ + "type" : "\"W\"", + "time" : $sformatf("%d", $time()), + "data" : $sformatf("0x%0x", axi_req_i.w.data), + "strb" : $sformatf("0x%0x", axi_req_i.w.strb), + "last" : $sformatf("0x%0x", axi_req_i.w.last), + "user" : $sformatf("0x%0x", axi_req_i.w.user) + }; + b_data = '{ + "type" : "\"B\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_resp_i.b.id), + "resp" : $sformatf("0x%0x", axi_resp_i.b.resp), + "user" : $sformatf("0x%0x", axi_resp_i.b.user) + }; + r_data = '{ + "type" : "\"R\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_resp_i.r.id), + "data" : $sformatf("0x%0x", axi_resp_i.r.data), + "resp" : $sformatf("0x%0x", axi_resp_i.r.resp), + "last" : $sformatf("0x%0x", axi_resp_i.r.last), + "user" : $sformatf("0x%0x", axi_resp_i.r.user) + }; + if (LogAW && axi_req_i.aw_valid && axi_resp_i.aw_ready) begin + aw_string = "{"; + foreach(aw_data[key]) aw_string = $sformatf("%s'%s': %s, ", aw_string, key, aw_data[key]); + aw_string = $sformatf("%s}", aw_string); + $fwrite(f, aw_string); + $fwrite(f, "\n"); + end + if (LogAR && axi_req_i.ar_valid && axi_resp_i.ar_ready) begin + ar_string = "{"; + foreach(ar_data[key]) ar_string = $sformatf("%s'%s': %s, ", ar_string, key, ar_data[key]); + ar_string = $sformatf("%s}", ar_string); + $fwrite(f, ar_string); + $fwrite(f, "\n"); + end + if (LogW && axi_req_i.w_valid && axi_resp_i.w_ready) begin + w_string = "{"; + foreach(w_data[key]) w_string = $sformatf("%s'%s': %s, ", w_string, key, w_data[key]); + w_string = $sformatf("%s}", w_string); + $fwrite(f, w_string); + $fwrite(f, "\n"); + end + if (LogB && axi_resp_i.b_valid && axi_req_i.b_ready) begin + b_string = "{"; + foreach(b_data[key]) b_string = $sformatf("%s'%s': %s, ", b_string, key, b_data[key]); + b_string = $sformatf("%s}", b_string); + $fwrite(f, b_string); + $fwrite(f, "\n"); + end + if (LogR && axi_resp_i.r_valid && axi_req_i.r_ready) begin + r_string = "{"; + foreach(r_data[key]) r_string = $sformatf("%s'%s': %s, ", r_string, key, r_data[key]); + r_string = $sformatf("%s}", r_string); + $fwrite(f, r_string); + $fwrite(f, "\n"); + end + end + end + final begin + $fclose(f); + end + +`endif + +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_dumper_intf #( + parameter BusName = "axi_bus", + parameter bit LogAW = 1'b1, + parameter bit LogAR = 1'b1, + parameter bit LogW = 1'b0, + parameter bit LogB = 1'b0, + parameter bit LogR = 1'b0, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS_DV.Monitor axi_bus +); + + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t axi_req; + axi_resp_t axi_resp; + + `AXI_ASSIGN_TO_REQ(axi_req, axi_bus) + `AXI_ASSIGN_TO_RESP(axi_resp, axi_bus) + + axi_dumper #( + .BusName ( BusName ), + .LogAW ( LogAW ), + .LogAR ( LogAR ), + .LogW ( LogW ), + .LogB ( LogB ), + .LogR ( LogR ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_dumper ( + .clk_i, + .rst_ni, + .axi_req_i ( axi_req ), + .axi_resp_i ( axi_resp ) + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_dw_downsizer.sv b/hw/vendor/pulp_platform_axi/src/axi_dw_downsizer.sv index 8000ba4f5..949b3caf3 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_dw_downsizer.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_dw_downsizer.sv @@ -188,8 +188,8 @@ module axi_dw_downsizer #( axi_err_slv #( .AxiIdWidth(AxiIdWidth ), .Resp (axi_pkg::RESP_SLVERR), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t ) + .axi_req_t (axi_mst_req_t ), + .axi_resp_t(axi_mst_resp_t ) ) i_axi_err_slv ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -216,8 +216,8 @@ module axi_dw_downsizer #( .b_chan_t (b_chan_t ), .ar_chan_t (ar_chan_t ), .r_chan_t (mst_r_chan_t ), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t), + .axi_req_t (axi_mst_req_t ), + .axi_resp_t (axi_mst_resp_t), .NoMstPorts (2 ), .MaxTrans (AxiMaxReads ), .SpillAw (1'b1 ) // Required to break dependency between AW and W channels @@ -340,6 +340,9 @@ module axi_dw_downsizer #( // Are we idle? assign idle_read_downsizer[t] = (r_state_q == R_IDLE) || (r_state_q == R_INJECT_AW); + // Byte-grouped data signal for the serialization step + slv_data_t r_data; + always_comb begin // Maintain state r_state_d = r_state_q; @@ -370,6 +373,9 @@ module axi_dw_downsizer #( r_req_d.ar_throw_error = 1'b0; end + // Initialize r_data + r_data = r_req_q.r.data; + case (r_state_q) R_IDLE : begin // Reset channels @@ -391,11 +397,12 @@ module axi_dw_downsizer #( r_state_d = R_PASSTHROUGH; // Save beat - r_req_d.ar = slv_req_i.ar ; - r_req_d.ar_valid = 1'b1 ; - r_req_d.burst_len = slv_req_i.ar.len ; - r_req_d.orig_ar_size = slv_req_i.ar.size; - r_req_d.injected_aw = 1'b0 ; + r_req_d.ar = slv_req_i.ar ; + r_req_d.ar_valid = 1'b1 ; + r_req_d.burst_len = slv_req_i.ar.len ; + r_req_d.orig_ar_size = slv_req_i.ar.size ; + r_req_d.injected_aw = 1'b0 ; + r_req_d.r.resp = axi_pkg::RESP_EXOKAY; case (r_req_d.ar.burst) axi_pkg::BURST_INCR : begin @@ -470,6 +477,7 @@ module axi_dw_downsizer #( r_req_d.burst_len = w_req_q.orig_aw_len ; r_req_d.orig_ar_size = w_req_q.orig_aw_size ; r_req_d.injected_aw = 1'b1 ; + r_req_d.r.resp = axi_pkg::RESP_EXOKAY ; // Default state r_state_d = R_PASSTHROUGH; @@ -533,12 +541,13 @@ module axi_dw_downsizer #( if (slv_r_valid_tran[t] && slv_r_ready_tran[t]) begin r_req_d.r = '0 ; r_req_d.r_valid = 1'b0; + r_data = '0 ; end // Request was accepted - if (!r_req_q.ar_valid) + if (!r_req_q.ar_valid) begin // Our turn - if ((idqueue_id == t) && idqueue_valid) + if ((idqueue_id == t) && idqueue_valid) begin // Ready to accept more data if (!slv_r_valid_tran[t] || (slv_r_valid_tran[t] && slv_r_ready_tran[t])) begin mst_r_ready_tran[t] = 1'b1; @@ -547,15 +556,14 @@ module axi_dw_downsizer #( automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiMstPortStrbWidth)-1:0]; automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiSlvPortStrbWidth)-1:0]; - automatic slv_data_t r_data = r_req_d.r.data; - // Serialization - for (int b = 0; b < AxiSlvPortStrbWidth; b++) + for (int b = 0; b < AxiSlvPortStrbWidth; b++) begin if ((b >= slv_port_offset) && (b - slv_port_offset < (1 << r_req_q.orig_ar_size)) && (b + mst_port_offset - slv_port_offset < AxiMstPortStrbWidth)) begin r_data[b] = mst_resp.r.data[8*(b + mst_port_offset - slv_port_offset) +: 8]; end + end r_req_d.burst_len = r_req_q.burst_len - 1 ; r_req_d.ar.len = r_req_q.ar.len - 1 ; @@ -576,33 +584,44 @@ module axi_dw_downsizer #( end endcase - if (r_req_q.burst_len == 0) + if (r_req_q.burst_len == 0) begin idqueue_pop[t] = 1'b1; + end case (r_state_q) - R_PASSTHROUGH : + R_PASSTHROUGH : begin // Forward data as soon as we can r_req_d.r_valid = 1'b1; + end - R_INCR_DOWNSIZE, R_SPLIT_INCR_DOWNSIZE: + R_INCR_DOWNSIZE, R_SPLIT_INCR_DOWNSIZE: begin // Forward when the burst is finished, or after filling up a word - if (r_req_q.burst_len == 0 || (aligned_addr(r_req_d.ar.addr, r_req_q.orig_ar_size) != aligned_addr(r_req_q.ar.addr, r_req_q.orig_ar_size))) + if (r_req_q.burst_len == 0 || + (aligned_addr(r_req_d.ar.addr, r_req_q.orig_ar_size) != + aligned_addr(r_req_q.ar.addr, r_req_q.orig_ar_size) )) begin r_req_d.r_valid = 1'b1; + end + end endcase // Trigger another burst request, if needed - if (r_state_q == R_SPLIT_INCR_DOWNSIZE) + if (r_state_q == R_SPLIT_INCR_DOWNSIZE) begin // Finished current burst, but whole transaction hasn't finished if (r_req_q.ar.len == '0 && r_req_q.burst_len != '0) begin r_req_d.ar_valid = !r_req_q.injected_aw; r_req_d.ar.len = (r_req_d.burst_len <= 255) ? r_req_d.burst_len : 255; end + end end end + end + end - if (slv_r_valid_tran[t] && slv_r_ready_tran[t]) - if (r_req_q.burst_len == '1) + if (slv_r_valid_tran[t] && slv_r_ready_tran[t]) begin + if (r_req_q.burst_len == '1) begin r_state_d = R_IDLE; + end + end end endcase end @@ -656,6 +675,9 @@ module axi_dw_downsizer #( .usage_o (/* Unused */ ) ); + // Byte-grouped data signal for the lane steering step + mst_data_t w_data; + always_comb begin inject_aw_into_ar_req = 1'b0; @@ -681,6 +703,9 @@ module axi_dw_downsizer #( mst_req.w_valid = '0; slv_resp_o.w_ready = '0; + // Initialize w_data + w_data = '0; + // B Channel (No latency) if (mst_resp.b_valid) begin // Merge response of this burst with prior one according to precedence rules. @@ -696,8 +721,9 @@ module axi_dw_downsizer #( mst_req.b_ready = slv_req_i.b_ready; // Got an ack on the B channel. Pop transaction. - if (mst_req.b_ready && mst_resp.b_valid) + if (mst_req.b_ready && mst_resp.b_valid) begin forward_b_beat_pop = 1'b1; + end end else begin // Otherwise, just acknowlegde the B beats slv_resp_o.b_valid = 1'b0 ; @@ -714,28 +740,28 @@ module axi_dw_downsizer #( case (w_state_q) W_PASSTHROUGH, W_INCR_DOWNSIZE, W_SPLIT_INCR_DOWNSIZE: begin // Request was accepted - if (!w_req_q.aw_valid) + if (!w_req_q.aw_valid) begin if (slv_req_i.w_valid) begin automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiMstPortStrbWidth)-1:0]; automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiSlvPortStrbWidth)-1:0]; - automatic mst_data_t w_data = mst_req.w.data; - // Valid output - mst_req.w_valid = 1'b1 ; + mst_req.w_valid = !(forward_b_beat_full && w_req_q.aw.len == 0); mst_req.w.last = w_req_q.aw.len == 0; mst_req.w.user = slv_req_i.w.user ; // Lane steering - for (int b = 0; b < AxiSlvPortStrbWidth; b++) + for (int b = 0; b < AxiSlvPortStrbWidth; b++) begin if ((b >= slv_port_offset) && (b - slv_port_offset < (1 << w_req_q.orig_aw_size)) && (b + mst_port_offset - slv_port_offset < AxiMstPortStrbWidth)) begin w_data[b + mst_port_offset - slv_port_offset] = slv_req_i.w.data[8*b +: 8]; mst_req.w.strb[b + mst_port_offset - slv_port_offset] = slv_req_i.w.strb[b] ; end + end mst_req.w.data = w_data; end + end // Acknowledgment if (mst_resp.w_ready && mst_req.w_valid) begin @@ -752,18 +778,23 @@ module axi_dw_downsizer #( endcase case (w_state_q) - W_PASSTHROUGH: + W_PASSTHROUGH: begin slv_resp_o.w_ready = 1'b1; + end - W_INCR_DOWNSIZE, W_SPLIT_INCR_DOWNSIZE: - if (w_req_q.burst_len == 0 || (aligned_addr(w_req_d.aw.addr, w_req_q.orig_aw_size) != aligned_addr(w_req_q.aw.addr, w_req_q.orig_aw_size))) + W_INCR_DOWNSIZE, W_SPLIT_INCR_DOWNSIZE: begin + if (w_req_q.burst_len == 0 || + (aligned_addr(w_req_d.aw.addr, w_req_q.orig_aw_size) != + aligned_addr(w_req_q.aw.addr, w_req_q.orig_aw_size) )) begin slv_resp_o.w_ready = 1'b1; + end + end endcase // Trigger another burst request, if needed - if (w_state_q == W_SPLIT_INCR_DOWNSIZE) + if (w_state_q == W_SPLIT_INCR_DOWNSIZE) begin // Finished current burst, but whole transaction hasn't finished - if (w_req_q.aw.len == '0 && w_req_q.burst_len != '0 && !forward_b_beat_full) begin + if (w_req_q.aw.len == '0 && w_req_q.burst_len != '0) begin w_req_d.aw_valid = 1'b1; w_req_d.aw.len = (w_req_d.burst_len <= 255) ? w_req_d.burst_len : 255; @@ -771,8 +802,9 @@ module axi_dw_downsizer #( forward_b_beat_i = 1'b0; forward_b_beat_push = 1'b1; end + end - if (w_req_q.burst_len == 0 && !forward_b_beat_full) begin + if (w_req_q.burst_len == 0) begin w_state_d = W_IDLE; forward_b_beat_push = 1'b1; @@ -785,13 +817,13 @@ module axi_dw_downsizer #( // Can start a new request as soon as w_state_d is W_IDLE if (w_state_d == W_IDLE) begin // Reset channels - w_req_d.aw = '0 ; - w_req_d.aw_valid = 1'b0 ; - w_req_d.aw_throw_error = 1'b0 ; - w_req_d.burst_resp = axi_pkg::RESP_OKAY; + w_req_d.aw = '0 ; + w_req_d.aw_valid = 1'b0 ; + w_req_d.aw_throw_error = 1'b0 ; + w_req_d.burst_resp = axi_pkg::RESP_EXOKAY; if (!forward_b_beat_full) begin - if (slv_req_i.aw_valid && slv_req_i.aw.atop[5]) begin // ATOP with an R response + if (slv_req_i.aw_valid && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // ATOP with an R response inject_aw_into_ar_req = 1'b1 ; slv_resp_o.aw_ready = inject_aw_into_ar_gnt; end else begin // Regular AW diff --git a/hw/vendor/pulp_platform_axi/src/axi_dw_upsizer.sv b/hw/vendor/pulp_platform_axi/src/axi_dw_upsizer.sv index 1d39ff8a3..9908b7860 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_dw_upsizer.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_dw_upsizer.sv @@ -185,8 +185,8 @@ module axi_dw_upsizer #( axi_err_slv #( .AxiIdWidth(AxiIdWidth ), .Resp (axi_pkg::RESP_SLVERR), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t ) + .axi_req_t (axi_mst_req_t ), + .axi_resp_t(axi_mst_resp_t ) ) i_axi_err_slv ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -213,8 +213,8 @@ module axi_dw_upsizer #( .b_chan_t (b_chan_t ), .ar_chan_t (ar_chan_t ), .r_chan_t (mst_r_chan_t ), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t), + .axi_req_t (axi_mst_req_t ), + .axi_resp_t (axi_mst_resp_t), .NoMstPorts (2 ), .MaxTrans (AxiMaxReads ), .SpillAw (1'b1 ) // Required to break dependency between AW and W channels @@ -322,6 +322,9 @@ module axi_dw_upsizer #( // Are we idle? assign idle_read_upsizer[t] = (r_state_q == R_IDLE) || (r_state_q == R_INJECT_AW); + // Byte-grouped data signal for the lane steering step + slv_data_t r_data; + always_comb begin // Maintain state r_state_d = r_state_q; @@ -353,6 +356,9 @@ module axi_dw_upsizer #( r_req_d.ar_throw_error = 1'b0; end + // Initialize r_data + r_data = '0; + case (r_state_q) R_IDLE : begin // Reset channels @@ -475,8 +481,6 @@ module axi_dw_upsizer #( automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiMstPortStrbWidth)-1:0]; automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiSlvPortStrbWidth)-1:0]; - automatic slv_data_t r_data = slv_r_tran[t].data; - // Valid output slv_r_valid_tran[t] = 1'b1 ; slv_r_tran[t].last = mst_resp.r.last && (r_req_q.burst_len == 0); @@ -544,6 +548,9 @@ module axi_dw_upsizer #( w_state_e w_state_d, w_state_q; + // Byte-grouped data signal for the serialization step + mst_data_t w_data; + always_comb begin inject_aw_into_ar_req = 1'b0; @@ -564,6 +571,9 @@ module axi_dw_upsizer #( mst_req.w_valid = w_req_q.w_valid; slv_resp_o.w_ready = '0 ; + // Initialize w_data + w_data = w_req_q.w.data; + // B Channel (No latency) slv_resp_o.b = mst_resp.b ; slv_resp_o.b_valid = mst_resp.b_valid ; @@ -579,6 +589,7 @@ module axi_dw_upsizer #( W_PASSTHROUGH, W_INCR_UPSIZE: begin // Got a grant on the W channel if (mst_req.w_valid && mst_resp.w_ready) begin + w_data = '0 ; w_req_d.w = '0 ; w_req_d.w_valid = 1'b0; end @@ -592,8 +603,6 @@ module axi_dw_upsizer #( automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiMstPortStrbWidth)-1:0]; automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiSlvPortStrbWidth)-1:0]; - automatic mst_data_t w_data = w_req_d.w.data; - // Serialization for (int b = 0; b < AxiMstPortStrbWidth; b++) if ((b >= mst_port_offset) && @@ -647,7 +656,7 @@ module axi_dw_upsizer #( w_req_d.w = '0 ; w_req_d.w_valid = 1'b0; - if (slv_req_i.aw_valid && slv_req_i.aw.atop[5]) begin // ATOP with an R response + if (slv_req_i.aw_valid && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // ATOP with an R response inject_aw_into_ar_req = 1'b1 ; slv_resp_o.aw_ready = inject_aw_into_ar_gnt; end else begin // Regular AW diff --git a/hw/vendor/pulp_platform_axi/src/axi_err_slv.sv b/hw/vendor/pulp_platform_axi/src/axi_err_slv.sv index f3c807dc3..e7719c429 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_err_slv.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_err_slv.sv @@ -18,20 +18,20 @@ module axi_err_slv #( parameter int unsigned AxiIdWidth = 0, // AXI ID Width - parameter type req_t = logic, // AXI 4 request struct, with atop field - parameter type resp_t = logic, // AXI 4 response struct + parameter type axi_req_t = logic, // AXI 4 request struct, with atop field + parameter type axi_resp_t = logic, // AXI 4 response struct parameter axi_pkg::resp_t Resp = axi_pkg::RESP_DECERR, // Error generated by this slave. parameter int unsigned RespWidth = 32'd64, // Data response width, gets zero extended or truncated to r.data. parameter logic [RespWidth-1:0] RespData = 64'hCA11AB1EBADCAB1E, // Hexvalue for data return value parameter bit ATOPs = 1'b1, // Activate support for ATOPs. Set to 1 if this slave could ever get an atomic AXI transaction. parameter int unsigned MaxTrans = 1 // Maximum # of accepted transactions before stalling ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic test_i, // Testmode enable + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, // Testmode enable // slave port - input req_t slv_req_i, - output resp_t slv_resp_o + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o ); typedef logic [AxiIdWidth-1:0] id_t; typedef struct packed { @@ -39,15 +39,15 @@ module axi_err_slv #( axi_pkg::len_t len; } r_data_t; - req_t err_req; - resp_t err_resp; + axi_req_t err_req; + axi_resp_t err_resp; if (ATOPs) begin axi_atop_filter #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( MaxTrans ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_atop_filter ( .clk_i, .rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_fifo.sv b/hw/vendor/pulp_platform_axi/src/axi_fifo.sv new file mode 100644 index 000000000..8e4cbdfb1 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_fifo.sv @@ -0,0 +1,256 @@ +// Copyright (c) 2014-2022 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Noah Huetter +// - Florian Zaruba +// - Fabian Schuiki + +// AXI4 Fifo +// +// Can be used to buffer transactions + +module axi_fifo #( + parameter int unsigned Depth = 32'd1, // Number of FiFo slots. + parameter bit FallThrough = 1'b0, // fifos are in fall-through mode + // AXI channel structs + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + // AXI request & response structs + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, + // slave port + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + // master port + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i +); + + if (Depth == '0) begin : gen_no_fifo + // degenerate case, connect input to output + assign mst_req_o = slv_req_i; + assign slv_resp_o = mst_resp_i; + end else begin : gen_axi_fifo + logic aw_fifo_empty, ar_fifo_empty, w_fifo_empty, r_fifo_empty, b_fifo_empty; + logic aw_fifo_full, ar_fifo_full, w_fifo_full, r_fifo_full, b_fifo_full; + + assign mst_req_o.aw_valid = ~aw_fifo_empty; + assign mst_req_o.ar_valid = ~ar_fifo_empty; + assign mst_req_o.w_valid = ~w_fifo_empty; + assign slv_resp_o.r_valid = ~r_fifo_empty; + assign slv_resp_o.b_valid = ~b_fifo_empty; + + assign slv_resp_o.aw_ready = ~aw_fifo_full; + assign slv_resp_o.ar_ready = ~ar_fifo_full; + assign slv_resp_o.w_ready = ~w_fifo_full; + assign mst_req_o.r_ready = ~r_fifo_full; + assign mst_req_o.b_ready = ~b_fifo_full; + + // A FiFo for each channel + fifo_v3 #( + .dtype(aw_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_aw_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (aw_fifo_full), + .empty_o (aw_fifo_empty), + .usage_o (), + .data_i (slv_req_i.aw), + .push_i (slv_req_i.aw_valid && slv_resp_o.aw_ready), + .data_o (mst_req_o.aw), + .pop_i (mst_req_o.aw_valid && mst_resp_i.aw_ready) + ); + fifo_v3 #( + .dtype(ar_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_ar_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (ar_fifo_full), + .empty_o (ar_fifo_empty), + .usage_o (), + .data_i (slv_req_i.ar), + .push_i (slv_req_i.ar_valid && slv_resp_o.ar_ready), + .data_o (mst_req_o.ar), + .pop_i (mst_req_o.ar_valid && mst_resp_i.ar_ready) + ); + fifo_v3 #( + .dtype(w_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_w_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (w_fifo_full), + .empty_o (w_fifo_empty), + .usage_o (), + .data_i (slv_req_i.w), + .push_i (slv_req_i.w_valid && slv_resp_o.w_ready), + .data_o (mst_req_o.w), + .pop_i (mst_req_o.w_valid && mst_resp_i.w_ready) + ); + fifo_v3 #( + .dtype(r_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_r_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (r_fifo_full), + .empty_o (r_fifo_empty), + .usage_o (), + .data_i (mst_resp_i.r), + .push_i (mst_resp_i.r_valid && mst_req_o.r_ready), + .data_o (slv_resp_o.r), + .pop_i (slv_resp_o.r_valid && slv_req_i.r_ready) + ); + fifo_v3 #( + .dtype(b_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_b_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (b_fifo_full), + .empty_o (b_fifo_empty), + .usage_o (), + .data_i (mst_resp_i.b), + .push_i (mst_resp_i.b_valid && mst_req_o.b_ready), + .data_o (slv_resp_o.b), + .pop_i (slv_resp_o.b_valid && slv_req_i.b_ready) + ); + end + + // Check the invariants + // pragma translate_off +`ifndef VERILATOR + initial begin + assert (Depth >= 0); + end +`endif + // pragma translate_on +endmodule + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +// interface wrapper +module axi_fifo_intf #( + parameter int unsigned ADDR_WIDTH = 0, // The address width. + parameter int unsigned DATA_WIDTH = 0, // The data width. + parameter int unsigned ID_WIDTH = 0, // The ID width. + parameter int unsigned USER_WIDTH = 0, // The user data width. + parameter int unsigned DEPTH = 0, // The number of FiFo slots. + parameter int unsigned FALL_THROUGH = 0 // FiFo in fall-through mode +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv, + AXI_BUS.Master mst +); + + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; + + `AXI_ASSIGN_TO_REQ(slv_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, slv_resp) + + `AXI_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_ASSIGN_TO_RESP(mst_resp, mst) + + axi_fifo #( + .Depth (DEPTH), + .FallThrough(FALL_THROUGH), + .aw_chan_t (aw_chan_t), + .w_chan_t (w_chan_t), + .b_chan_t (b_chan_t), + .ar_chan_t (ar_chan_t), + .r_chan_t (r_chan_t), + .axi_req_t (axi_req_t), + .axi_resp_t (axi_resp_t) + ) i_axi_fifo ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i (slv_req), + .slv_resp_o(slv_resp), + .mst_req_o (mst_req), + .mst_resp_i(mst_resp) + ); + + // Check the invariants. + // pragma translate_off +`ifndef VERILATOR + initial begin + assert (ADDR_WIDTH > 0) + else $fatal(1, "Wrong addr width parameter"); + assert (DATA_WIDTH > 0) + else $fatal(1, "Wrong data width parameter"); + assert (ID_WIDTH > 0) + else $fatal(1, "Wrong id width parameter"); + assert (USER_WIDTH > 0) + else $fatal(1, "Wrong user width parameter"); + assert (slv.AXI_ADDR_WIDTH == ADDR_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_DATA_WIDTH == DATA_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_ID_WIDTH == ID_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_USER_WIDTH == USER_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_ADDR_WIDTH == ADDR_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_DATA_WIDTH == DATA_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_ID_WIDTH == ID_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_USER_WIDTH == USER_WIDTH) + else $fatal(1, "Wrong interface definition"); + end +`endif + // pragma translate_on +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_from_mem.sv b/hw/vendor/pulp_platform_axi/src/axi_from_mem.sv new file mode 100644 index 000000000..466a384e3 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_from_mem.sv @@ -0,0 +1,124 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Christopher Reinwardt +// - Nicole Narr + +`include "axi/typedef.svh" + +/// Protocol adapter which translates memory requests to the AXI4 protocol. +/// +/// This module acts like an SRAM and makes AXI4 requests downstream. +/// +/// Supports multiple outstanding requests and will have responses for reads **and** writes. +/// Response latency is not fixed and for sure **not 1** and depends on the AXI4 memory system. +/// The `mem_rsp_valid_o` can have multiple cycles of latency from the corresponding `mem_gnt_o`. +module axi_from_mem #( + /// Memory request address width. + parameter int unsigned MemAddrWidth = 32'd0, + /// AXI4-Lite address width. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width in bit of the memory request data **and** the Axi4-Lite data channels. + parameter int unsigned DataWidth = 32'd0, + /// How many requests can be in flight at the same time. (Depth of the response mux FIFO). + parameter int unsigned MaxRequests = 32'd0, + /// Protection signal the module should emit on the AXI4 transactions. + parameter axi_pkg::prot_t AxiProt = 3'b000, + /// AXI4 request struct definition. + parameter type axi_req_t = logic, + /// AXI4 response struct definition. + parameter type axi_rsp_t = logic +) ( + /// Clock input, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Memory slave port, request is active. + input logic mem_req_i, + /// Memory slave port, request address. + /// + /// Byte address, will be extended or truncated to match `AxiAddrWidth`. + input logic [MemAddrWidth-1:0] mem_addr_i, + /// Memory slave port, request is a write. + /// + /// `0`: Read request. + /// `1`: Write request. + input logic mem_we_i, + /// Memory salve port, write data for request. + input logic [DataWidth-1:0] mem_wdata_i, + /// Memory slave port, write byte enable for request. + /// + /// Active high. + input logic [DataWidth/8-1:0] mem_be_i, + /// Memory request is granted. + output logic mem_gnt_o, + /// Memory slave port, response is valid. For each request, regardless if read or write, + /// this will be active once for one cycle. + output logic mem_rsp_valid_o, + /// Memory slave port, response read data. This is forwarded directly from the AXI4-Lite + /// `R` channel. Only valid for responses generated by a read request. + output logic [DataWidth-1:0] mem_rsp_rdata_o, + /// Memory request encountered an error. This is forwarded from the AXI4-Lite error response. + output logic mem_rsp_error_o, + /// AXI4 master port, slave aw cache signal + input axi_pkg::cache_t slv_aw_cache_i, + /// AXI4 master port, slave ar cache signal + input axi_pkg::cache_t slv_ar_cache_i, + /// AXI4 master port, request output. + output axi_req_t axi_req_o, + /// AXI4 master port, response input. + input axi_rsp_t axi_rsp_i +); + + `AXI_LITE_TYPEDEF_ALL(axi_lite, logic [AxiAddrWidth-1:0], logic [DataWidth-1:0], logic [DataWidth/8-1:0]) + axi_lite_req_t axi_lite_req; + axi_lite_resp_t axi_lite_rsp; + + axi_lite_from_mem #( + .MemAddrWidth ( MemAddrWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .DataWidth ( DataWidth ), + .MaxRequests ( MaxRequests ), + .AxiProt ( AxiProt ), + .axi_req_t ( axi_lite_req_t ), + .axi_rsp_t ( axi_lite_resp_t ) + ) i_axi_lite_from_mem ( + .clk_i, + .rst_ni, + .mem_req_i, + .mem_addr_i, + .mem_we_i, + .mem_wdata_i, + .mem_be_i, + .mem_gnt_o, + .mem_rsp_valid_o, + .mem_rsp_rdata_o, + .mem_rsp_error_o, + .axi_req_o ( axi_lite_req ), + .axi_rsp_i ( axi_lite_rsp ) + ); + + axi_lite_to_axi #( + .AxiDataWidth ( DataWidth ), + .req_lite_t ( axi_lite_req_t ), + .resp_lite_t ( axi_lite_resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_rsp_t ) + ) i_axi_lite_to_axi ( + .slv_req_lite_i ( axi_lite_req ), + .slv_resp_lite_o ( axi_lite_rsp ), + .slv_aw_cache_i, + .slv_ar_cache_i, + .mst_req_o ( axi_req_o ), + .mst_resp_i ( axi_rsp_i ) + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_id_remap.sv b/hw/vendor/pulp_platform_axi/src/axi_id_remap.sv new file mode 100644 index 000000000..85dc66e0d --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_id_remap.sv @@ -0,0 +1,661 @@ +// Copyright (c) 2014-2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger +// - Florian Zaruba + +`include "common_cells/registers.svh" + +/// Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. +/// +/// This module is designed to remap an overly wide, sparsely used ID space to a narrower, densely +/// used ID space. This scenario occurs, for example, when an AXI master has wide ID ports but +/// effectively only uses a (not necessarily contiguous) subset of IDs. +/// +/// This module retains the independence of IDs. That is, if two transactions have different IDs at +/// the slave port of this module, they are guaranteed to have different IDs at the master port of +/// this module. This implies a lower bound on the [width of IDs on the master +/// port](#parameter.AxiMstPortIdWidth). If you require narrower master port IDs and can forgo ID +/// independence, use [`axi_id_serialize`](module.axi_id_serialize) instead. +/// +/// Internally, a [table is used for remapping IDs](module.axi_id_remap_table). +module axi_id_remap #( + /// ID width of the AXI4+ATOP slave port. + parameter int unsigned AxiSlvPortIdWidth = 32'd0, + /// Maximum number of different IDs that can be in flight at the slave port. Reads and writes are + /// counted separately (except for ATOPs, which count as both read and write). + /// + /// It is legal for upstream to have transactions with more unique IDs than the maximum given by + /// this parameter in flight, but a transaction exceeding the maximum will be stalled until all + /// transactions of another ID complete. + /// + /// The maximum value of this parameter is `2**AxiSlvPortIdWidth`. + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID. + /// + /// It is legal for upstream to have more transactions than the maximum given by this parameter in + /// flight for any ID, but a transaction exceeding the maximum will be stalled until another + /// transaction with the same ID completes. + parameter int unsigned AxiMaxTxnsPerId = 32'd0, + /// ID width of the AXI4+ATOP master port. + /// + /// The minimum value of this parameter is the ceiled binary logarithm of `AxiSlvPortMaxUniqIds`, + /// because IDs at the master port must be wide enough to represent IDs up to + /// `AxiSlvPortMaxUniqIds-1`. + /// + /// If master IDs are wider than the minimum, they are extended by prepending zeros. + parameter int unsigned AxiMstPortIdWidth = 32'd0, + /// Request struct type of the AXI4+ATOP slave port. + /// + /// The width of all IDs in this struct must match `AxiSlvPortIdWidth`. + parameter type slv_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port. + /// + /// The width of all IDs in this struct must match `AxiSlvPortIdWidth`. + parameter type slv_resp_t = logic, + /// Request struct type of the AXI4+ATOP master port + /// + /// The width of all IDs in this struct must match `AxiMstPortIdWidth`. + parameter type mst_req_t = logic, + /// Response struct type of the AXI4+ATOP master port + /// + /// The width of all IDs in this struct must match `AxiMstPortIdWidth`. + parameter type mst_resp_t = logic +) ( + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Slave port request + input slv_req_t slv_req_i, + /// Slave port response + output slv_resp_t slv_resp_o, + /// Master port request + output mst_req_t mst_req_o, + /// Master port response + input mst_resp_t mst_resp_i +); + + // Feed all signals that are not ID or flow control of AW and AR through. + assign mst_req_o.aw.addr = slv_req_i.aw.addr; + assign mst_req_o.aw.len = slv_req_i.aw.len; + assign mst_req_o.aw.size = slv_req_i.aw.size; + assign mst_req_o.aw.burst = slv_req_i.aw.burst; + assign mst_req_o.aw.lock = slv_req_i.aw.lock; + assign mst_req_o.aw.cache = slv_req_i.aw.cache; + assign mst_req_o.aw.prot = slv_req_i.aw.prot; + assign mst_req_o.aw.qos = slv_req_i.aw.qos; + assign mst_req_o.aw.region = slv_req_i.aw.region; + assign mst_req_o.aw.atop = slv_req_i.aw.atop; + assign mst_req_o.aw.user = slv_req_i.aw.user; + + assign mst_req_o.w = slv_req_i.w; + assign mst_req_o.w_valid = slv_req_i.w_valid; + assign slv_resp_o.w_ready = mst_resp_i.w_ready; + + assign slv_resp_o.b.resp = mst_resp_i.b.resp; + assign slv_resp_o.b.user = mst_resp_i.b.user; + assign slv_resp_o.b_valid = mst_resp_i.b_valid; + assign mst_req_o.b_ready = slv_req_i.b_ready; + + assign mst_req_o.ar.addr = slv_req_i.ar.addr; + assign mst_req_o.ar.len = slv_req_i.ar.len; + assign mst_req_o.ar.size = slv_req_i.ar.size; + assign mst_req_o.ar.burst = slv_req_i.ar.burst; + assign mst_req_o.ar.lock = slv_req_i.ar.lock; + assign mst_req_o.ar.cache = slv_req_i.ar.cache; + assign mst_req_o.ar.prot = slv_req_i.ar.prot; + assign mst_req_o.ar.qos = slv_req_i.ar.qos; + assign mst_req_o.ar.region = slv_req_i.ar.region; + assign mst_req_o.ar.user = slv_req_i.ar.user; + + assign slv_resp_o.r.data = mst_resp_i.r.data; + assign slv_resp_o.r.resp = mst_resp_i.r.resp; + assign slv_resp_o.r.last = mst_resp_i.r.last; + assign slv_resp_o.r.user = mst_resp_i.r.user; + assign slv_resp_o.r_valid = mst_resp_i.r_valid; + assign mst_req_o.r_ready = slv_req_i.r_ready; + + + // Remap tables keep track of in-flight bursts and their input and output IDs. + localparam int unsigned IdxWidth = cf_math_pkg::idx_width(AxiSlvPortMaxUniqIds); + typedef logic [AxiSlvPortMaxUniqIds-1:0] field_t; + typedef logic [AxiSlvPortIdWidth-1:0] id_inp_t; + typedef logic [IdxWidth-1:0] idx_t; + field_t wr_free, rd_free, both_free; + id_inp_t rd_push_inp_id; + idx_t wr_free_oup_id, rd_free_oup_id, both_free_oup_id, + wr_push_oup_id, rd_push_oup_id, + wr_exists_id, rd_exists_id; + logic wr_exists, rd_exists, + wr_exists_full, rd_exists_full, + wr_full, rd_full, + wr_push, rd_push; + + axi_id_remap_table #( + .InpIdWidth ( AxiSlvPortIdWidth ), + .MaxUniqInpIds ( AxiSlvPortMaxUniqIds ), + .MaxTxnsPerId ( AxiMaxTxnsPerId ) + ) i_wr_table ( + .clk_i, + .rst_ni, + .free_o ( wr_free ), + .free_oup_id_o ( wr_free_oup_id ), + .full_o ( wr_full ), + .push_i ( wr_push ), + .push_inp_id_i ( slv_req_i.aw.id ), + .push_oup_id_i ( wr_push_oup_id ), + .exists_inp_id_i ( slv_req_i.aw.id ), + .exists_o ( wr_exists ), + .exists_oup_id_o ( wr_exists_id ), + .exists_full_o ( wr_exists_full ), + .pop_i ( slv_resp_o.b_valid && slv_req_i.b_ready ), + .pop_oup_id_i ( mst_resp_i.b.id[IdxWidth-1:0] ), + .pop_inp_id_o ( slv_resp_o.b.id ) + ); + axi_id_remap_table #( + .InpIdWidth ( AxiSlvPortIdWidth ), + .MaxUniqInpIds ( AxiSlvPortMaxUniqIds ), + .MaxTxnsPerId ( AxiMaxTxnsPerId ) + ) i_rd_table ( + .clk_i, + .rst_ni, + .free_o ( rd_free ), + .free_oup_id_o ( rd_free_oup_id ), + .full_o ( rd_full ), + .push_i ( rd_push ), + .push_inp_id_i ( rd_push_inp_id ), + .push_oup_id_i ( rd_push_oup_id ), + .exists_inp_id_i ( slv_req_i.ar.id ), + .exists_o ( rd_exists ), + .exists_oup_id_o ( rd_exists_id ), + .exists_full_o ( rd_exists_full ), + .pop_i ( slv_resp_o.r_valid && slv_req_i.r_ready && slv_resp_o.r.last ), + .pop_oup_id_i ( mst_resp_i.r.id[IdxWidth-1:0] ), + .pop_inp_id_o ( slv_resp_o.r.id ) + ); + assign both_free = wr_free & rd_free; + lzc #( + .WIDTH ( AxiSlvPortMaxUniqIds ), + .MODE ( 1'b0 ) + ) i_lzc ( + .in_i ( both_free ), + .cnt_o ( both_free_oup_id ), + .empty_o ( /* unused */ ) + ); + + // Zero-extend output IDs if the output IDs is are wider than the IDs from the tables. + localparam ZeroWidth = AxiMstPortIdWidth - IdxWidth; + assign mst_req_o.ar.id = {{ZeroWidth{1'b0}}, rd_push_oup_id}; + assign mst_req_o.aw.id = {{ZeroWidth{1'b0}}, wr_push_oup_id}; + + // Handle requests. + enum logic [1:0] {Ready, HoldAR, HoldAW, HoldAx} state_d, state_q; + idx_t ar_id_d, ar_id_q, + aw_id_d, aw_id_q; + logic ar_prio_d, ar_prio_q; + always_comb begin + mst_req_o.aw_valid = 1'b0; + slv_resp_o.aw_ready = 1'b0; + wr_push = 1'b0; + wr_push_oup_id = '0; + mst_req_o.ar_valid = 1'b0; + slv_resp_o.ar_ready = 1'b0; + rd_push = 1'b0; + rd_push_inp_id = '0; + rd_push_oup_id = '0; + ar_id_d = ar_id_q; + aw_id_d = aw_id_q; + ar_prio_d = ar_prio_q; + state_d = state_q; + + unique case (state_q) + Ready: begin + // Reads + if (slv_req_i.ar_valid) begin + // If a burst with the same input ID is already in flight or there are free output IDs: + if ((rd_exists && !rd_exists_full) || (!rd_exists && !rd_full)) begin + // Determine the output ID: if another in-flight burst had the same input ID, we must + // reuse its output ID to maintain ordering; else, we assign the next free ID. + rd_push_inp_id = slv_req_i.ar.id; + rd_push_oup_id = rd_exists ? rd_exists_id : rd_free_oup_id; + // Forward the AR and push a new entry to the read table. + mst_req_o.ar_valid = 1'b1; + rd_push = 1'b1; + end + end + + // Writes + if (slv_req_i.aw_valid) begin + // If this is not an ATOP that gives rise to an R response, we can handle it in isolation + // on the write direction. + if (!slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin + // If a burst with the same input ID is already in flight or there are free output IDs: + if ((wr_exists && !wr_exists_full) || (!wr_exists && !wr_full)) begin + // Determine the output ID: if another in-flight burst had the same input ID, we must + // reuse its output ID to maintain ordering; else, we assign the next free ID. + wr_push_oup_id = wr_exists ? wr_exists_id : wr_free_oup_id; + // Forward the AW and push a new entry to the write table. + mst_req_o.aw_valid = 1'b1; + wr_push = 1'b1; + end + // If this is an ATOP that gives rise to an R response, we must remap to an ID that is + // free on both read and write direction and push also to the read table. + // Only allowed if AR does not have arbitration priority + end else if (!(ar_prio_q && mst_req_o.ar_valid)) begin + // Nullify a potential AR at our output. This is legal in this state. + mst_req_o.ar_valid = 1'b0; + slv_resp_o.ar_ready = 1'b0; + rd_push = 1'b0; + if ((|both_free)) begin + // Use an output ID that is free in both directions. + wr_push_oup_id = both_free_oup_id; + rd_push_inp_id = slv_req_i.aw.id; + rd_push_oup_id = both_free_oup_id; + // Forward the AW and push a new entry to both tables. + mst_req_o.aw_valid = 1'b1; + rd_push = 1'b1; + wr_push = 1'b1; + // Give AR priority in the next cycle (so ATOPs cannot infinitely preempt ARs). + ar_prio_d = 1'b1; + end + end + end + + // Hold AR, AW, or both if they are valid but not yet ready. + if (mst_req_o.ar_valid) begin + slv_resp_o.ar_ready = mst_resp_i.ar_ready; + if (!mst_resp_i.ar_ready) begin + ar_id_d = rd_push_oup_id; + end + end + if (mst_req_o.aw_valid) begin + slv_resp_o.aw_ready = mst_resp_i.aw_ready; + if (!mst_resp_i.aw_ready) begin + aw_id_d = wr_push_oup_id; + end + end + if ({mst_req_o.ar_valid, mst_resp_i.ar_ready, + mst_req_o.aw_valid, mst_resp_i.aw_ready} == 4'b1010) begin + state_d = HoldAx; + end else if ({mst_req_o.ar_valid, mst_resp_i.ar_ready} == 2'b10) begin + state_d = HoldAR; + end else if ({mst_req_o.aw_valid, mst_resp_i.aw_ready} == 2'b10) begin + state_d = HoldAW; + end else begin + state_d = Ready; + end + + if (mst_req_o.ar_valid && mst_resp_i.ar_ready) begin + ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle. + end + end + + HoldAR: begin + // Drive `mst_req_o.ar.id` through `rd_push_oup_id`. + rd_push_oup_id = ar_id_q; + mst_req_o.ar_valid = 1'b1; + slv_resp_o.ar_ready = mst_resp_i.ar_ready; + if (mst_resp_i.ar_ready) begin + state_d = Ready; + ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle. + end + end + + HoldAW: begin + // Drive mst_req_o.aw.id through `wr_push_oup_id`. + wr_push_oup_id = aw_id_q; + mst_req_o.aw_valid = 1'b1; + slv_resp_o.aw_ready = mst_resp_i.aw_ready; + if (mst_resp_i.aw_ready) begin + state_d = Ready; + end + end + + HoldAx: begin + rd_push_oup_id = ar_id_q; + mst_req_o.ar_valid = 1'b1; + slv_resp_o.ar_ready = mst_resp_i.ar_ready; + wr_push_oup_id = aw_id_q; + mst_req_o.aw_valid = 1'b1; + slv_resp_o.aw_ready = mst_resp_i.aw_ready; + unique case ({mst_resp_i.ar_ready, mst_resp_i.aw_ready}) + 2'b01: state_d = HoldAR; + 2'b10: state_d = HoldAW; + 2'b11: state_d = Ready; + default: /*do nothing / stay in this state*/; + endcase + if (mst_resp_i.ar_ready) begin + ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle. + end + end + + default: state_d = Ready; + endcase + end + + // Registers + `FFARN(ar_id_q, ar_id_d, '0, clk_i, rst_ni) + `FFARN(ar_prio_q, ar_prio_d, 1'b0, clk_i, rst_ni) + `FFARN(aw_id_q, aw_id_d, '0, clk_i, rst_ni) + `FFARN(state_q, state_d, Ready, clk_i, rst_ni) + + // pragma translate_off + `ifndef VERILATOR + initial begin : p_assert + assert(AxiSlvPortIdWidth > 32'd0) + else $fatal(1, "Parameter AxiSlvPortIdWidth has to be larger than 0!"); + assert(AxiMstPortIdWidth >= IdxWidth) + else $fatal(1, "Parameter AxiMstPortIdWidth has to be at least IdxWidth!"); + assert (AxiSlvPortMaxUniqIds > 0) + else $fatal(1, "Parameter AxiSlvPortMaxUniqIds has to be larger than 0!"); + assert (AxiSlvPortMaxUniqIds <= 2**AxiSlvPortIdWidth) + else $fatal(1, "Parameter AxiSlvPortMaxUniqIds may be at most 2**AxiSlvPortIdWidth!"); + assert (AxiMaxTxnsPerId > 0) + else $fatal(1, "Parameter AxiMaxTxnsPerId has to be larger than 0!"); + assert($bits(slv_req_i.aw.addr) == $bits(mst_req_o.aw.addr)) + else $fatal(1, "AXI AW address widths are not equal!"); + assert($bits(slv_req_i.w.data) == $bits(mst_req_o.w.data)) + else $fatal(1, "AXI W data widths are not equal!"); + assert($bits(slv_req_i.w.user) == $bits(mst_req_o.w.user)) + else $fatal(1, "AXI W user widths are not equal!"); + assert($bits(slv_req_i.ar.addr) == $bits(mst_req_o.ar.addr)) + else $fatal(1, "AXI AR address widths are not equal!"); + assert($bits(slv_resp_o.r.data) == $bits(mst_resp_i.r.data)) + else $fatal(1, "AXI R data widths are not equal!"); + assert ($bits(slv_req_i.aw.id) == AxiSlvPortIdWidth); + assert ($bits(slv_resp_o.b.id) == AxiSlvPortIdWidth); + assert ($bits(slv_req_i.ar.id) == AxiSlvPortIdWidth); + assert ($bits(slv_resp_o.r.id) == AxiSlvPortIdWidth); + assert ($bits(mst_req_o.aw.id) == AxiMstPortIdWidth); + assert ($bits(mst_resp_i.b.id) == AxiMstPortIdWidth); + assert ($bits(mst_req_o.ar.id) == AxiMstPortIdWidth); + assert ($bits(mst_resp_i.r.id) == AxiMstPortIdWidth); + end + default disable iff (!rst_ni); + assert property (@(posedge clk_i) slv_req_i.aw_valid && slv_resp_o.aw_ready + |-> mst_req_o.aw_valid && mst_resp_i.aw_ready); + assert property (@(posedge clk_i) mst_resp_i.b_valid && mst_req_o.b_ready + |-> slv_resp_o.b_valid && slv_req_i.b_ready); + assert property (@(posedge clk_i) slv_req_i.ar_valid && slv_resp_o.ar_ready + |-> mst_req_o.ar_valid && mst_resp_i.ar_ready); + assert property (@(posedge clk_i) mst_resp_i.r_valid && mst_req_o.r_ready + |-> slv_resp_o.r_valid && slv_req_i.r_ready); + assert property (@(posedge clk_i) slv_resp_o.r_valid + |-> slv_resp_o.r.last == mst_resp_i.r.last); + assert property (@(posedge clk_i) mst_req_o.ar_valid && !mst_resp_i.ar_ready + |=> mst_req_o.ar_valid && $stable(mst_req_o.ar.id)); + assert property (@(posedge clk_i) mst_req_o.aw_valid && !mst_resp_i.aw_ready + |=> mst_req_o.aw_valid && $stable(mst_req_o.aw.id)); + `endif + // pragma translate_on +endmodule + +/// Internal module of [`axi_id_remap`](module.axi_id_remap): Table to remap input to output IDs. +/// +/// This module contains a table indexed by the output ID (type `idx_t`). Each table entry has two +/// fields: the input ID and a counter that records how many transactions with the input and output +/// ID of the entry are in-flight. +/// +/// The mapping from input and output IDs is injective. Therefore, when the table contains an entry +/// for an input ID with non-zero counter value, subsequent input IDs must use the same entry and +/// thus the same output ID. +/// +/// ## Relation of types and table layout +/// ![diagram of table](axi_id_remap_table.svg) +/// +/// ## Complexity +/// This module has: +/// - `MaxUniqInpIds * InpIdWidth * clog2(MaxTxnsPerId)` flip flops; +/// - `MaxUniqInpIds` comparators of width `InpIdWidth`; +/// - 2 leading-zero counters of width `MaxUniqInpIds`. +module axi_id_remap_table #( + /// Width of input IDs, therefore width of `id_inp_t`. + parameter int unsigned InpIdWidth = 32'd0, + /// Maximum number of different input IDs that can be in-flight. This defines the number of remap + /// table entries. + /// + /// The maximum value of this parameter is `2**InpIdWidth`. + parameter int unsigned MaxUniqInpIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID. + parameter int unsigned MaxTxnsPerId = 32'd0, + /// Derived (**=do not override**) type of input IDs. + localparam type id_inp_t = logic [InpIdWidth-1:0], + /// Derived (**=do not override**) width of table index (ceiled binary logarithm of + /// `MaxUniqInpIds`). + localparam int unsigned IdxWidth = cf_math_pkg::idx_width(MaxUniqInpIds), + /// Derived (**=do not override**) type of table index (width = `IdxWidth`). + localparam type idx_t = logic [IdxWidth-1:0], + /// Derived (**=do not override**) type with one bit per table entry (thus also output ID). + localparam type field_t = logic [MaxUniqInpIds-1:0] +) ( + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + + /// One bit per table entry / output ID that indicates whether the entry is free. + output field_t free_o, + /// Lowest free output ID. Only valid if the table is not full (i.e., `!full_o`). + output idx_t free_oup_id_o, + /// Indicates whether the table is full. + output logic full_o, + + /// Push an input/output ID pair to the table. + input logic push_i, + /// Input ID to be pushed. If the table already contains this ID, its counter must be smaller than + /// `MaxTxnsPerId`. + input id_inp_t push_inp_id_i, + /// Output ID to be pushed. If the table already contains the input ID to be pushed, the output + /// ID **must** match the output ID of the existing entry with the same input ID. + input idx_t push_oup_id_i, + + /// Input ID to look up in the table. + input id_inp_t exists_inp_id_i, + /// Indicates whether the given input ID exists in the table. + output logic exists_o, + /// The output ID of the given input ID. Only valid if the input ID exists (i.e., `exists_o`). + output idx_t exists_oup_id_o, + /// Indicates whether the maximum number of transactions for the given input ID is reached. Only + /// valid if the input ID exists (i.e., `exists_o`). + output logic exists_full_o, + + /// Pop an output ID from the table. This reduces the counter for the table index given in + /// `pop_oup_id_i` by one. + input logic pop_i, + /// Output ID to be popped. The counter for this ID must be larger than `0`. + input idx_t pop_oup_id_i, + /// Input ID corresponding to the popped output ID. + output id_inp_t pop_inp_id_o +); + + /// Counter width, derived to hold numbers up to `MaxTxnsPerId`. + localparam int unsigned CntWidth = $clog2(MaxTxnsPerId+1); + /// Counter that tracks the number of in-flight transactions with an ID. + typedef logic [CntWidth-1:0] cnt_t; + + /// Type of a table entry. + typedef struct packed { + id_inp_t inp_id; + cnt_t cnt; + } entry_t; + + // Table indexed by output IDs that contains the corresponding input IDs + entry_t [MaxUniqInpIds-1:0] table_d, table_q; + + // Determine lowest free output ID. + for (genvar i = 0; i < MaxUniqInpIds; i++) begin : gen_free_o + assign free_o[i] = table_q[i].cnt == '0; + end + lzc #( + .WIDTH ( MaxUniqInpIds ), + .MODE ( 1'b0 ) + ) i_lzc_free ( + .in_i ( free_o ), + .cnt_o ( free_oup_id_o ), + .empty_o ( full_o ) + ); + + // Determine the input ID for a given output ID. + if (MaxUniqInpIds == 1) begin : gen_pop_for_single_unique_inp_id + assign pop_inp_id_o = table_q[0].inp_id; + end else begin : gen_pop_for_multiple_unique_inp_ids + assign pop_inp_id_o = table_q[pop_oup_id_i].inp_id; + end + + // Determine if given output ID is already used and, if it is, by which input ID. + field_t match; + for (genvar i = 0; i < MaxUniqInpIds; i++) begin : gen_match + assign match[i] = table_q[i].cnt > 0 && table_q[i].inp_id == exists_inp_id_i; + end + logic no_match; + lzc #( + .WIDTH ( MaxUniqInpIds ), + .MODE ( 1'b0 ) + ) i_lzc_match ( + .in_i ( match ), + .cnt_o ( exists_oup_id_o ), + .empty_o ( no_match ) + ); + assign exists_o = ~no_match; + if (MaxUniqInpIds == 1) begin : gen_exists_full_for_single_unique_inp_id + assign exists_full_o = table_q[0].cnt == MaxTxnsPerId; + end else begin : gen_exists_full_for_multiple_unique_inp_ids + assign exists_full_o = table_q[exists_oup_id_o].cnt == MaxTxnsPerId; + end + + // Push and pop table entries. + always_comb begin + table_d = table_q; + if (push_i) begin + table_d[push_oup_id_i].inp_id = push_inp_id_i; + table_d[push_oup_id_i].cnt += 1; + end + if (pop_i) begin + table_d[pop_oup_id_i].cnt -= 1; + end + end + + // Registers + `FFARN(table_q, table_d, '0, clk_i, rst_ni) + + // Assertions + // pragma translate_off + `ifndef VERILATOR + default disable iff (!rst_ni); + assume property (@(posedge clk_i) push_i |-> + table_q[push_oup_id_i].cnt == '0 || table_q[push_oup_id_i].inp_id == push_inp_id_i) + else $error("Push must be to empty output ID or match existing input ID!"); + assume property (@(posedge clk_i) push_i |-> table_q[push_oup_id_i].cnt < MaxTxnsPerId) + else $error("Maximum number of in-flight bursts must not be exceeded!"); + assume property (@(posedge clk_i) pop_i |-> table_q[pop_oup_id_i].cnt > 0) + else $error("Pop must target output ID with non-zero counter!"); + assume property (@(posedge clk_i) $onehot0(match)) + else $error("Input ID in table must be unique!"); + initial begin + assert (InpIdWidth > 0); + assert (MaxUniqInpIds > 0); + assert (MaxUniqInpIds <= (1 << InpIdWidth)); + assert (MaxTxnsPerId > 0); + assert (IdxWidth >= 1); + end + `endif + // pragma translate_on + +endmodule + + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// Interface variant of [`axi_id_remap`](module.axi_id_remap). +/// +/// See the documentation of the main module for the definition of ports and parameters. +module axi_id_remap_intf #( + parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0, + parameter int unsigned AXI_SLV_PORT_MAX_UNIQ_IDS = 32'd0, + parameter int unsigned AXI_MAX_TXNS_PER_ID = 32'd0, + parameter int unsigned AXI_MST_PORT_ID_WIDTH = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave slv, + AXI_BUS.Master mst +); + typedef logic [AXI_SLV_PORT_ID_WIDTH-1:0] slv_id_t; + typedef logic [AXI_MST_PORT_ID_WIDTH-1:0] mst_id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t; + typedef logic [AXI_USER_WIDTH-1:0] axi_user_t; + + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, axi_addr_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_W_CHAN_T(slv_w_chan_t, axi_data_t, axi_strb_t, axi_user_t) + `AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, axi_addr_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, axi_data_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, slv_w_chan_t, slv_ar_chan_t) + `AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t) + + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, axi_addr_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_W_CHAN_T(mst_w_chan_t, axi_data_t, axi_strb_t, axi_user_t) + `AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, axi_addr_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, axi_data_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, mst_w_chan_t, mst_ar_chan_t) + `AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t) + + slv_req_t slv_req; + slv_resp_t slv_resp; + mst_req_t mst_req; + mst_resp_t mst_resp; + + `AXI_ASSIGN_TO_REQ(slv_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, slv_resp) + `AXI_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_ASSIGN_TO_RESP(mst_resp, mst) + + axi_id_remap #( + .AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ), + .AxiSlvPortMaxUniqIds ( AXI_SLV_PORT_MAX_UNIQ_IDS ), + .AxiMaxTxnsPerId ( AXI_MAX_TXNS_PER_ID ), + .AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ) + ) i_axi_id_remap ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_resp_o ( slv_resp ), + .mst_req_o ( mst_req ), + .mst_resp_i ( mst_resp ) + ); + // pragma translate_off + `ifndef VERILATOR + initial begin + assert (slv.AXI_ID_WIDTH == AXI_SLV_PORT_ID_WIDTH); + assert (slv.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH); + assert (slv.AXI_DATA_WIDTH == AXI_DATA_WIDTH); + assert (slv.AXI_USER_WIDTH == AXI_USER_WIDTH); + assert (mst.AXI_ID_WIDTH == AXI_MST_PORT_ID_WIDTH); + assert (mst.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH); + assert (mst.AXI_DATA_WIDTH == AXI_DATA_WIDTH); + assert (mst.AXI_USER_WIDTH == AXI_USER_WIDTH); + end + `endif + // pragma translate_on +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_id_serialize.sv b/hw/vendor/pulp_platform_axi/src/axi_id_serialize.sv new file mode 100644 index 000000000..4aa068683 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_id_serialize.sv @@ -0,0 +1,439 @@ +// Copyright (c) 2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Andreas Kurth +// - Paul Scheffler +// - Michael Rogenmoser + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +/// Reduce AXI IDs by serializing transactions when necessary. +/// +/// This module is designed to remap a wide ID space to an arbitrarily narrow ID space. If +/// necessary, this module maps two different IDs at its slave port to the same ID at its master +/// port, thereby constraining the order of those transactions and in this sense *serializing* them. +/// If the independence of IDs needs to be retained at the cost of a wider ID space at the master +/// port, use [`axi_id_remap`](module.axi_id_remap) instead. +/// +/// This module contains one [`axi_serializer`](module.axi_serializer) per master port ID (given by +/// the `AxiMstPortMaxUniqIds parameter`). +module axi_id_serialize #( + /// ID width of the AXI4+ATOP slave port + parameter int unsigned AxiSlvPortIdWidth = 32'd0, + /// ID width of the AXI4+ATOP master port + parameter int unsigned AxiMstPortIdWidth = 32'd0, + /// Maximum number of different IDs that can be in flight at the master port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// The maximum value of this parameter is `2**AxiMstPortIdWidth`. + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the master port. + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + /// Address width of both AXI4+ATOP ports + parameter int unsigned AxiAddrWidth = 32'd0, + /// User width of both AXI4+ATOP ports + parameter int unsigned AxiUserWidth = 32'd0, + /// Request struct type of the AXI4+ATOP slave port + parameter type slv_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type slv_resp_t = logic, + /// Request struct type of the AXI4+ATOP master port + parameter type mst_req_t = logic, + /// Response struct type of the AXI4+ATOP master port + parameter type mst_resp_t = logic, + /// A custom offset (modulo `AxiMstPortMaxUniqIds`, ignored for input IDs remapped through + /// `IdMap`) for the assigned output IDs. + parameter int unsigned MstIdBaseOffset = 32'd0, + /// Explicit input-output ID map. If an input ID `id` does not appear in this mapping (default), + /// it is simply mapped to the output ID `id % AxiMstPortMaxUniqIds`. If `id` appears in more + /// than one entry, it is matched to the *last* matching entry's output ID. + /// Number of Entries in the explicit ID map (default: None) + parameter int unsigned IdMapNumEntries = 32'd0, + /// Explicit ID map; index [0] in each entry is the input ID to match, index [1] the output ID. + parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: {32'b0, 32'b0}}, + /// Spill AR and AW + parameter bit SpillAx = 1'b1, + // unused parameters, no longer needed, left for backwards-compatibility + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + parameter int unsigned AxiDataWidth = 32'd0, + parameter bit AtopSupport = 1'b1 +) ( + /// Rising-edge clock of both ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Slave port request + input slv_req_t slv_req_i, + /// Slave port response + output slv_resp_t slv_resp_o, + /// Master port request + output mst_req_t mst_req_o, + /// Master port response + input mst_resp_t mst_resp_i +); + + /// Number of bits of the slave port ID that determine the mapping to the master port ID + localparam int unsigned SelectWidth = cf_math_pkg::idx_width(AxiMstPortMaxUniqIds); + /// Slice of slave port IDs that determines the master port ID + typedef logic [SelectWidth-1:0] select_t; + + /// ID at the master port + typedef logic [AxiMstPortIdWidth-1:0] mst_id_t; + /// Address in any AXI channel + typedef logic [AxiAddrWidth-1:0] addr_t; + /// User signal in any AXI channel + typedef logic [AxiUserWidth-1:0] user_t; + + /// AW channel at master port + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t) + /// AR channel at master port + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t) + + /// Type for slave ID map + typedef mst_id_t [2**AxiSlvPortIdWidth-1:0] slv_id_map_t; + + /// Resolve target output ID for each possible input ID as a parameter + function automatic slv_id_map_t map_slv_ids(); + slv_id_map_t ret = '0; + // Populate output with default mapping, including `MstIdBaseOffset` + for (int unsigned i = 0; i < 2**AxiSlvPortIdWidth; ++i) + ret[i] = (i + MstIdBaseOffset) % AxiMstPortMaxUniqIds; + // For each explicitly mapped input ID, set the desired output ID + for (int unsigned i = 0; i < IdMapNumEntries; ++i) + ret[IdMap[i][0]] = IdMap[i][1]; + return ret; + endfunction + + /// Input-to-output ID map used + localparam slv_id_map_t SlvIdMap = map_slv_ids(); + + select_t slv_aw_select, slv_ar_select, slv_b_select, slv_r_select; + assign slv_aw_select = select_t'(SlvIdMap[slv_req_i.aw.id]); + assign slv_ar_select = select_t'(SlvIdMap[slv_req_i.ar.id]); + + slv_req_t [AxiMstPortMaxUniqIds-1:0] to_serializer_reqs; + slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps; + + logic [AxiMstPortMaxUniqIds-1:0] to_serializer_resps_b_valid, + to_serializer_resps_r_valid; + + // Convert B response valid to index for selection + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_slv_b_select ( + .onehot(to_serializer_resps_b_valid), + .bin (slv_b_select) + ); + + // Convert R response valid to index for selection + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_slv_r_select ( + .onehot(to_serializer_resps_r_valid), + .bin (slv_r_select) + ); + + for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin + assign to_serializer_resps_b_valid[i] = to_serializer_resps[i].b_valid; + assign to_serializer_resps_r_valid[i] = to_serializer_resps[i].r_valid; + end + + // Due to static ID mapping, ID consistency checking is not needed. + always_comb begin + // AW, W, AR + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + to_serializer_reqs[i] = slv_req_i; // assigns.aw, .w, .ar + to_serializer_reqs[i].aw_valid = '0; + to_serializer_reqs[i].w_valid = '0; + to_serializer_reqs[i].ar_valid = '0; + end + + // Only pass AW to the selected serializer + to_serializer_reqs[slv_aw_select].aw_valid = slv_req_i.aw_valid; + slv_resp_o.aw_ready = to_serializer_resps[slv_aw_select].aw_ready; + + // W is always in order (i.e., serialized) and does not have ID field, pass through + slv_resp_o.w_ready = mst_resp_i.w_ready; + + // Only pass AR to the selected serializer + to_serializer_reqs[slv_ar_select].ar_valid = slv_req_i.ar_valid; + slv_resp_o.ar_ready = to_serializer_resps[slv_ar_select].ar_ready; + + // Recombine responses, only one will be active at a time + // B, R (these are passed through or both gated inside the serializer) + slv_resp_o.b_valid = |to_serializer_resps_b_valid; + slv_resp_o.b = to_serializer_resps[slv_b_select].b; + slv_resp_o.r_valid = |to_serializer_resps_r_valid; + slv_resp_o.r = to_serializer_resps[slv_r_select].r; + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + to_serializer_reqs[i].b_ready = slv_req_i.b_ready; + to_serializer_reqs[i].r_ready = slv_req_i.r_ready; + end + end + + slv_req_t [AxiMstPortMaxUniqIds-1:0] tmp_serializer_reqs; + slv_resp_t [AxiMstPortMaxUniqIds-1:0] tmp_serializer_resps; + mst_req_t [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs; + mst_resp_t [AxiMstPortMaxUniqIds-1:0] from_serializer_resps; + + // One serializer per desired ouput ID + for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin : gen_serializers + // serializer takes care to ensure unique IDs for ATOPs + axi_serializer #( + .MaxReadTxns ( AxiMstPortMaxTxnsPerId ), + .MaxWriteTxns ( AxiMstPortMaxTxnsPerId ), + .AxiIdWidth ( AxiSlvPortIdWidth ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ) + ) i_axi_serializer ( + .clk_i, + .rst_ni, + .slv_req_i ( to_serializer_reqs[i] ), + .slv_resp_o ( to_serializer_resps[i] ), + .mst_req_o ( tmp_serializer_reqs[i] ), + .mst_resp_i ( tmp_serializer_resps[i] ) + ); + // Assign desired output ID + always_comb begin + `AXI_SET_REQ_STRUCT(from_serializer_reqs[i], tmp_serializer_reqs[i]) + from_serializer_reqs[i].aw.id = i; + from_serializer_reqs[i].ar.id = i; + `AXI_SET_RESP_STRUCT(tmp_serializer_resps[i], from_serializer_resps[i]) + // Zero-extend response IDs. + tmp_serializer_resps[i].b.id = '0; + tmp_serializer_resps[i].r.id = '0; + end + end + + logic [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs_aw_valid, + from_serializer_reqs_ar_valid, + from_serializer_reqs_b_ready, + from_serializer_reqs_r_ready; + + select_t mst_aw_select, mst_ar_select; + + // Convert AW request valid to index for selection + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_mst_aw_select ( + .onehot(from_serializer_reqs_aw_valid), + .bin (mst_aw_select) + ); + + // Convert AR request valid to index for selection + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_mst_ar_select ( + .onehot(from_serializer_reqs_ar_valid), + .bin (mst_ar_select) + ); + + for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin + assign from_serializer_reqs_aw_valid[i] = from_serializer_reqs[i].aw_valid; + assign from_serializer_reqs_ar_valid[i] = from_serializer_reqs[i].ar_valid; + end + + mst_req_t mst_req; + logic mst_aw_ready, mst_ar_ready; + + always_comb begin + // Recombine AW requests, only one active at a time + mst_req.aw_valid = |from_serializer_reqs_aw_valid; + mst_req.aw = from_serializer_reqs[mst_aw_select].aw; + + // W is always in order (i.e., serialized) and does not have ID field, pass through + mst_req.w_valid = slv_req_i.w_valid; + mst_req.w = slv_req_i.w; + + // Recombine AR requests, only one active at a time + mst_req.ar_valid = |from_serializer_reqs_ar_valid; + mst_req.ar = from_serializer_reqs[mst_ar_select].ar; + + // AW/AR ready distributed, only one request active at a time + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + from_serializer_resps[i].aw_ready = mst_aw_ready; + from_serializer_resps[i].ar_ready = mst_ar_ready; + end + + // Distribute B and R responses to corresponding serializers + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + from_serializer_reqs_b_ready[i] = from_serializer_reqs[i].b_ready; + from_serializer_reqs_r_ready[i] = from_serializer_reqs[i].r_ready; + from_serializer_resps[i].b_valid = '0; + from_serializer_resps[i].r_valid = '0; + from_serializer_resps[i].b = mst_resp_i.b; + from_serializer_resps[i].r = mst_resp_i.r; + end + from_serializer_resps[mst_resp_i.b.id].b_valid = mst_resp_i.b_valid; + mst_req.b_ready = from_serializer_reqs[mst_resp_i.b.id].b_ready; + from_serializer_resps[mst_resp_i.r.id].r_valid = mst_resp_i.r_valid; + mst_req.r_ready = from_serializer_reqs[mst_resp_i.r.id].r_ready; + end + + // Optional spill register to help prevent timing loops + spill_register #( + .T ( mst_aw_t ), + .Bypass( ~SpillAx ) + ) i_aw_spill_reg ( + .clk_i, + .rst_ni, + .valid_i( mst_req.aw_valid ), + .ready_o( mst_aw_ready ), + .data_i ( mst_req.aw), + .valid_o( mst_req_o.aw_valid ), + .ready_i( mst_resp_i.aw_ready ), + .data_o ( mst_req_o.aw ) + ); + + // Optional spill register to help prevent timing loops + spill_register #( + .T ( mst_ar_t ), + .Bypass( ~SpillAx ) + ) i_ar_spill_reg ( + .clk_i, + .rst_ni, + .valid_i( mst_req.ar_valid ), + .ready_o( mst_ar_ready ), + .data_i ( mst_req.ar ), + .valid_o( mst_req_o.ar_valid ), + .ready_i( mst_resp_i.ar_ready ), + .data_o ( mst_req_o.ar ) + ); + + `AXI_ASSIGN_W_STRUCT(mst_req_o.w, mst_req.w) + assign mst_req_o.w_valid = mst_req.w_valid; + assign mst_req_o.b_ready = mst_req.b_ready; + assign mst_req_o.r_ready = mst_req.r_ready; + + // pragma translate_off + `ifndef VERILATOR + initial begin : p_assert + assert(AxiMstPortMaxUniqIds > 32'd0) + else $fatal(1, "AxiMstPortMaxUniqIds has to be > 0."); + assert(2**(AxiMstPortIdWidth) >= AxiMstPortMaxUniqIds) + else $fatal(1, "Not enought Id width on MST port to map all ID's."); + assert(AxiSlvPortIdWidth > 32'd0) + else $fatal(1, "Parameter AxiSlvPortIdWidth has to be larger than 0!"); + assert(AxiMstPortIdWidth) + else $fatal(1, "Parameter AxiMstPortIdWidth has to be larger than 0!"); + assert(AxiMstPortIdWidth <= AxiSlvPortIdWidth) + else $fatal(1, "Downsize implies that AxiMstPortIdWidth <= AxiSlvPortIdWidth!"); + assert($bits(slv_req_i.aw.addr) == $bits(mst_req_o.aw.addr)) + else $fatal(1, "AXI AW address widths are not equal!"); + assert($bits(slv_req_i.w.data) == $bits(mst_req_o.w.data)) + else $fatal(1, "AXI W data widths are not equal!"); + assert($bits(slv_req_i.ar.addr) == $bits(mst_req_o.ar.addr)) + else $fatal(1, "AXI AR address widths are not equal!"); + assert($bits(slv_resp_o.r.data) == $bits(mst_resp_i.r.data)) + else $fatal(1, "AXI R data widths are not equal!"); + end + `endif + // pragma translate_on +endmodule + + +/// Interface variant of [`axi_id_serialize`](module.axi_id_serialize). +/// +/// See the documentation of the main module for the definition of ports and parameters. +module axi_id_serialize_intf #( + parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0, + parameter int unsigned AXI_MST_PORT_ID_WIDTH = 32'd0, + parameter int unsigned AXI_MST_PORT_MAX_UNIQ_IDS = 32'd0, + parameter int unsigned AXI_MST_PORT_MAX_TXNS_PER_ID = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0, + parameter int unsigned MST_ID_BASE_OFFSET = 32'd0, + parameter int unsigned ID_MAP_NUM_ENTRIES = 32'd0, + parameter int unsigned ID_MAP [ID_MAP_NUM_ENTRIES-1:0][0:1] = '{default: {32'b0, 32'b0}}, + parameter bit SPILL_AX = 1'b1, + // Now unused, kept for backward compatibility + parameter int unsigned AXI_SLV_PORT_MAX_TXNS = 32'd0 +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave slv, + AXI_BUS.Master mst +); + + typedef logic [AXI_SLV_PORT_ID_WIDTH-1:0] slv_id_t; + typedef logic [AXI_MST_PORT_ID_WIDTH-1:0] mst_id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t) + `AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_t, w_t, slv_ar_t) + `AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_t, slv_r_t) + + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(mst_b_t, mst_id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t) + `AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_t, w_t, mst_ar_t) + `AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_t, mst_r_t) + + slv_req_t slv_req; + slv_resp_t slv_resp; + mst_req_t mst_req; + mst_resp_t mst_resp; + + `AXI_ASSIGN_TO_REQ(slv_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, slv_resp) + `AXI_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_ASSIGN_TO_RESP(mst_resp, mst) + + axi_id_serialize #( + .AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ), + .AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ), + .AxiMstPortMaxUniqIds ( AXI_MST_PORT_MAX_UNIQ_IDS ), + .AxiMstPortMaxTxnsPerId ( AXI_MST_PORT_MAX_TXNS_PER_ID ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiUserWidth ( AXI_USER_WIDTH ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ), + .MstIdBaseOffset ( MST_ID_BASE_OFFSET ), + .IdMapNumEntries ( ID_MAP_NUM_ENTRIES ), + .IdMap ( ID_MAP ), + .SpillAx ( SPILL_AX ) + ) i_axi_id_serialize ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_resp_o ( slv_resp ), + .mst_req_o ( mst_req ), + .mst_resp_i ( mst_resp ) + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin + assert (slv.AXI_ID_WIDTH == AXI_SLV_PORT_ID_WIDTH); + assert (slv.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH); + assert (slv.AXI_DATA_WIDTH == AXI_DATA_WIDTH); + assert (slv.AXI_USER_WIDTH == AXI_USER_WIDTH); + assert (mst.AXI_ID_WIDTH == AXI_MST_PORT_ID_WIDTH); + assert (mst.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH); + assert (mst.AXI_DATA_WIDTH == AXI_DATA_WIDTH); + assert (mst.AXI_USER_WIDTH == AXI_USER_WIDTH); + end +`endif +// pragma translate_on +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_interleaved_xbar.sv b/hw/vendor/pulp_platform_axi/src/axi_interleaved_xbar.sv new file mode 100644 index 000000000..83d215df7 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_interleaved_xbar.sv @@ -0,0 +1,418 @@ +// Copyright (c) 2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Wolfgang Roenninger +// - Andreas Kurth +// - Florian Zaruba +// - Thomas Benz + +/// Interleaved version of the crossbar. This module is experimental; use at your own risk. +module axi_interleaved_xbar +import cf_math_pkg::idx_width; +#( + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter bit ATOPs = 1'b1, + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] Connectivity = '1, + parameter type slv_aw_chan_t = logic, + parameter type mst_aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type slv_b_chan_t = logic, + parameter type mst_b_chan_t = logic, + parameter type slv_ar_chan_t = logic, + parameter type mst_ar_chan_t = logic, + parameter type slv_r_chan_t = logic, + parameter type mst_r_chan_t = logic, + parameter type slv_req_t = logic, + parameter type slv_resp_t = logic, + parameter type mst_req_t = logic, + parameter type mst_resp_t = logic, + parameter type rule_t = axi_pkg::xbar_rule_64_t, + // don't overwrite + parameter int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + input logic interleaved_mode_ena_i, + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, +`ifdef VCS + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif +); + + typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; + // to account for the decoding error slave +`ifdef VCS + localparam int unsigned MstPortsIdxWidthOne = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts + 1)); + typedef logic [MstPortsIdxWidthOne-1:0] mst_port_idx_t; +`else + typedef logic [idx_width(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; +`endif + + // signals from the axi_demuxes, one index more for decode error + slv_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; + slv_resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; + + // workaround for issue #133 (problem with vsim 10.6c) + localparam int unsigned cfg_NoMstPorts = Cfg.NoMstPorts; + + // signals into the axi_muxes, are of type slave as the multiplexer extends the ID + slv_req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; + slv_resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; + + // interleaved address modify + slv_req_t [Cfg.NoSlvPorts-1:0] slv_reqs_mod; + + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux +`ifdef VCS + logic [MstPortsIdxWidth-1:0] dec_aw, dec_ar; + logic [MstPortsIdxWidth-1:0] dec_inter_aw, dec_inter_ar; +`else + logic [idx_width(Cfg.NoMstPorts)-1:0] dec_aw, dec_ar; + logic [idx_width(Cfg.NoMstPorts)-1:0] dec_inter_aw, dec_inter_ar; +`endif + mst_port_idx_t slv_aw_select, slv_ar_select; + logic dec_aw_valid, dec_aw_error; + logic dec_ar_valid, dec_ar_error; + + localparam int unsigned BankSelLow = 'd16; + localparam int unsigned BankSelHigh = BankSelLow + MstPortsIdxWidth; + + // interleaved select (4kiB blocks) + assign dec_inter_aw = slv_ports_req_i[i].aw.addr[BankSelLow +: MstPortsIdxWidth]; + assign dec_inter_ar = slv_ports_req_i[i].ar.addr[BankSelLow +: MstPortsIdxWidth]; + + // interleaved address modify + always_comb begin : proc_modify_addr_interleaved + slv_reqs_mod[i] = slv_ports_req_i[i]; + + // only modify if interleaved mode is active + if (interleaved_mode_ena_i == 1'b1) begin + // modify AW address by removing bank bits + slv_reqs_mod[i].aw.addr = { {(MstPortsIdxWidth){1'b0}}, + slv_ports_req_i[i].aw.addr[Cfg.AxiAddrWidth-1:BankSelHigh], + slv_ports_req_i[i].aw.addr[BankSelLow-1:0] }; + + // modify AR address by removing bank bits + slv_reqs_mod[i].ar.addr = { {(MstPortsIdxWidth){1'b0}}, + slv_ports_req_i[i].ar.addr[Cfg.AxiAddrWidth-1:BankSelHigh], + slv_ports_req_i[i].ar.addr[BankSelLow-1:0] }; + end + end + + addr_decode #( + .NoIndices ( Cfg.NoMstPorts ), + .NoRules ( Cfg.NoAddrRules ), + .addr_t ( addr_t ), + .rule_t ( rule_t ) + ) i_axi_aw_decode ( + .addr_i ( slv_ports_req_i[i].aw.addr ), + .addr_map_i ( addr_map_i ), + .idx_o ( dec_aw ), + .dec_valid_o ( dec_aw_valid ), + .dec_error_o ( dec_aw_error ), + .en_default_idx_i ( en_default_mst_port_i[i] ), + .default_idx_i ( default_mst_port_i[i] ) + ); + + addr_decode #( + .NoIndices ( Cfg.NoMstPorts ), + .addr_t ( addr_t ), + .NoRules ( Cfg.NoAddrRules ), + .rule_t ( rule_t ) + ) i_axi_ar_decode ( + .addr_i ( slv_ports_req_i[i].ar.addr ), + .addr_map_i ( addr_map_i ), + .idx_o ( dec_ar ), + .dec_valid_o ( dec_ar_valid ), + .dec_error_o ( dec_ar_error ), + .en_default_idx_i ( en_default_mst_port_i[i] ), + .default_idx_i ( default_mst_port_i[i] ) + ); + + always_comb begin : ax_select + if (interleaved_mode_ena_i == 1'b1) begin + slv_aw_select = dec_inter_aw; + slv_ar_select = dec_inter_ar; + end else begin + slv_aw_select = (dec_aw_error) ? + mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_aw); + slv_ar_select = (dec_ar_error) ? + mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_ar); + end + end + + // make sure that the default slave does not get changed, if there is an unserved Ax + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + default disable iff (~rst_ni); + default_aw_mst_port_en: assert property( + @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) + |=> $stable(en_default_mst_port_i[i])) + else $fatal (1, $sformatf({"It is not allowed to change the default mst port enable,", + "when there is an unserved Aw beat. Slave Port: %0d"}, i)); + default_aw_mst_port: assert property( + @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) + |=> $stable(default_mst_port_i[i])) + else $fatal (1, $sformatf({"It is not allowed to change the default mst port", + "when there is an unserved Aw beat. Slave Port: %0d"}, i)); + default_ar_mst_port_en: assert property( + @(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready) + |=> $stable(en_default_mst_port_i[i])) + else $fatal (1, $sformatf({"It is not allowed to change the enable, when", + "there is an unserved Ar beat. Slave Port: %0d"}, i)); + default_ar_mst_port: assert property( + @(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready) + |=> $stable(default_mst_port_i[i])) + else $fatal (1, $sformatf({"It is not allowed to change the default mst port", + "when there is an unserved Ar beat. Slave Port: %0d"}, i)); + `endif + `endif + // pragma translate_on + axi_demux #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), // ID Width + .AtopSupport ( ATOPs ), + .aw_chan_t ( slv_aw_chan_t ), // AW Channel Type + .w_chan_t ( w_chan_t ), // W Channel Type + .b_chan_t ( slv_b_chan_t ), // B Channel Type + .ar_chan_t ( slv_ar_chan_t ), // AR Channel Type + .r_chan_t ( slv_r_chan_t ), // R Channel Type + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), + .NoMstPorts ( Cfg.NoMstPorts + 1 ), + .MaxTrans ( Cfg.MaxMstTrans ), + .AxiLookBits ( Cfg.AxiIdUsedSlvPorts ), + .UniqueIds ( Cfg.UniqueIds ), + .SpillAw ( Cfg.LatencyMode[9] ), + .SpillW ( Cfg.LatencyMode[8] ), + .SpillB ( Cfg.LatencyMode[7] ), + .SpillAr ( Cfg.LatencyMode[6] ), + .SpillR ( Cfg.LatencyMode[5] ) + ) i_axi_demux ( + .clk_i, // Clock + .rst_ni, // Asynchronous reset active low + .test_i, // Testmode enable + .slv_req_i ( slv_reqs_mod[i] ), + .slv_aw_select_i ( slv_aw_select ), + .slv_ar_select_i ( slv_ar_select ), + .slv_resp_o ( slv_ports_resp_o[i] ), + .mst_reqs_o ( slv_reqs[i] ), + .mst_resps_i ( slv_resps[i] ) + ); + + axi_err_slv #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .ATOPs ( ATOPs ), + .MaxTrans ( 4 ) // Transactions terminate at this slave, so minimize + // resource consumption by accepting only a few + // transactions at a time. + ) i_axi_err_slv ( + .clk_i, // Clock + .rst_ni, // Asynchronous reset active low + .test_i, // Testmode enable + // slave port + .slv_req_i ( slv_reqs[i][Cfg.NoMstPorts] ), + .slv_resp_o ( slv_resps[i][cfg_NoMstPorts] ) + ); + end + + // cross all channels + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_xbar_slv_cross + for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross + if (Connectivity[i][j]) begin : gen_connection + assign mst_reqs[j][i] = slv_reqs[i][j]; + assign slv_resps[i][j] = mst_resps[j][i]; + + end else begin : gen_no_connection + assign mst_reqs[j][i] = '0; + axi_err_slv #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .ATOPs ( ATOPs ), + .MaxTrans ( 1 ) + ) i_axi_err_slv ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ) + ); + end + end + end + + for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_mst_port_mux + axi_mux #( + .SlvAxiIDWidth ( Cfg.AxiIdWidthSlvPorts ), // ID width of the slave ports + .slv_aw_chan_t ( slv_aw_chan_t ), // AW Channel Type, slave ports + .mst_aw_chan_t ( mst_aw_chan_t ), // AW Channel Type, master port + .w_chan_t ( w_chan_t ), // W Channel Type, all ports + .slv_b_chan_t ( slv_b_chan_t ), // B Channel Type, slave ports + .mst_b_chan_t ( mst_b_chan_t ), // B Channel Type, master port + .slv_ar_chan_t ( slv_ar_chan_t ), // AR Channel Type, slave ports + .mst_ar_chan_t ( mst_ar_chan_t ), // AR Channel Type, master port + .slv_r_chan_t ( slv_r_chan_t ), // R Channel Type, slave ports + .mst_r_chan_t ( mst_r_chan_t ), // R Channel Type, master port + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ), + .NoSlvPorts ( Cfg.NoSlvPorts ), // Number of Masters for the module + .MaxWTrans ( Cfg.MaxSlvTrans ), + .FallThrough ( Cfg.FallThrough ), + .SpillAw ( Cfg.LatencyMode[4] ), + .SpillW ( Cfg.LatencyMode[3] ), + .SpillB ( Cfg.LatencyMode[2] ), + .SpillAr ( Cfg.LatencyMode[1] ), + .SpillR ( Cfg.LatencyMode[0] ) + ) i_axi_mux ( + .clk_i, // Clock + .rst_ni, // Asynchronous reset active low + .test_i, // Test Mode enable + .slv_reqs_i ( mst_reqs[i] ), + .slv_resps_o ( mst_resps[i] ), + .mst_req_o ( mst_ports_req_o[i] ), + .mst_resp_i ( mst_ports_resp_i[i] ) + ); + end + + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + initial begin : check_params + id_slv_req_ports: assert ($bits(slv_ports_req_i[0].aw.id ) == Cfg.AxiIdWidthSlvPorts) else + $fatal(1, $sformatf("Slv_req and aw_chan id width not equal.")); + id_slv_resp_ports: assert ($bits(slv_ports_resp_o[0].r.id) == Cfg.AxiIdWidthSlvPorts) else + $fatal(1, $sformatf("Slv_req and aw_chan id width not equal.")); + end + `endif + `endif + // pragma translate_on +endmodule : axi_interleaved_xbar + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_interleaved_xbar_intf +import cf_math_pkg::idx_width; +#( + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter int unsigned AXI_USER_WIDTH = 0, + parameter bit ATOPS = 1'b1, + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] CONNECTIVITY = '1, + parameter type rule_t = axi_pkg::xbar_rule_64_t +`ifdef VCS + , localparam int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +`endif +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv_ports [Cfg.NoSlvPorts-1:0], + AXI_BUS.Master mst_ports [Cfg.NoMstPorts-1:0], + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, + input logic interleaved_mode_ena_i, +`ifdef VCS + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif +); + + localparam int unsigned AxiIdWidthMstPorts = Cfg.AxiIdWidthSlvPorts + $clog2(Cfg.NoSlvPorts); + + typedef logic [AxiIdWidthMstPorts -1:0] id_mst_t; + typedef logic [Cfg.AxiIdWidthSlvPorts -1:0] id_slv_t; + typedef logic [Cfg.AxiAddrWidth -1:0] addr_t; + typedef logic [Cfg.AxiDataWidth -1:0] data_t; + typedef logic [Cfg.AxiDataWidth/8 -1:0] strb_t; + typedef logic [AXI_USER_WIDTH -1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, addr_t, id_mst_t, user_t) + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, addr_t, id_slv_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, id_mst_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, id_slv_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, addr_t, id_mst_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, addr_t, id_slv_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, data_t, id_mst_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, data_t, id_slv_t, user_t) + `AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, w_chan_t, mst_ar_chan_t) + `AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, w_chan_t, slv_ar_chan_t) + `AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t) + `AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t) + + mst_req_t [Cfg.NoMstPorts-1:0] mst_reqs; + mst_resp_t [Cfg.NoMstPorts-1:0] mst_resps; + slv_req_t [Cfg.NoSlvPorts-1:0] slv_reqs; + slv_resp_t [Cfg.NoSlvPorts-1:0] slv_resps; + + for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_assign_mst + `AXI_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) + `AXI_ASSIGN_TO_RESP(mst_resps[i], mst_ports[i]) + end + + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_assign_slv + `AXI_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i]) + `AXI_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i]) + end + + axi_interleaved_xbar #( + .Cfg (Cfg), + .ATOPs ( ATOPS ), + .Connectivity ( CONNECTIVITY ), + .slv_aw_chan_t ( slv_aw_chan_t ), + .mst_aw_chan_t ( mst_aw_chan_t ), + .w_chan_t ( w_chan_t ), + .slv_b_chan_t ( slv_b_chan_t ), + .mst_b_chan_t ( mst_b_chan_t ), + .slv_ar_chan_t ( slv_ar_chan_t ), + .mst_ar_chan_t ( mst_ar_chan_t ), + .slv_r_chan_t ( slv_r_chan_t ), + .mst_r_chan_t ( mst_r_chan_t ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ), + .rule_t ( rule_t ) + ) i_interleaved_xbar ( + .clk_i, + .rst_ni, + .test_i, + .slv_ports_req_i (slv_reqs ), + .slv_ports_resp_o (slv_resps), + .mst_ports_req_o (mst_reqs ), + .mst_ports_resp_i (mst_resps), + .addr_map_i, + .interleaved_mode_ena_i, + .en_default_mst_port_i, + .default_mst_port_i + ); + +endmodule : axi_interleaved_xbar_intf diff --git a/hw/vendor/pulp_platform_axi/src/axi_intf.sv b/hw/vendor/pulp_platform_axi/src/axi_intf.sv index 0d43ffbae..273e9baae 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_intf.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_intf.sv @@ -98,6 +98,14 @@ interface AXI_BUS #( output r_id, r_data, r_resp, r_last, r_user, r_valid, input r_ready ); + modport Monitor ( + input aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_ready, + w_data, w_strb, w_last, w_user, w_valid, w_ready, + b_id, b_resp, b_user, b_valid, b_ready, + ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_ready, + r_id, r_data, r_resp, r_last, r_user, r_valid, r_ready + ); + endinterface @@ -241,6 +249,16 @@ interface AXI_BUS_DV #( assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_last))); assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_user))); assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> r_valid)); + // Address-Channel Assertions: The address of the last beat of a burst must be on the same 4 KiB + // page as the address of the first beat of a burst. Bus is always word-aligned, word < page. + assert property (@(posedge clk_i) aw_valid |-> (aw_burst != axi_pkg::BURST_INCR) || ( + axi_pkg::beat_addr(aw_addr, aw_size, aw_len, aw_burst, 0) >> 12 == // lowest beat address + axi_pkg::beat_addr(aw_addr, aw_size, aw_len, aw_burst, aw_len) >> 12 // highest beat address + )) else $error("AW burst crossing 4 KiB page boundary detected, which is illegal!"); + assert property (@(posedge clk_i) ar_valid |-> (aw_burst != axi_pkg::BURST_INCR) || ( + axi_pkg::beat_addr(ar_addr, ar_size, ar_len, ar_burst, 0) >> 12 == // lowest beat address + axi_pkg::beat_addr(ar_addr, ar_size, ar_len, ar_burst, ar_len) >> 12 // highest beat address + )) else $error("AR burst crossing 4 KiB page boundary detected, which is illegal!"); `endif // pragma translate_on @@ -441,6 +459,14 @@ interface AXI_LITE #( output r_data, r_resp, r_valid, input r_ready ); + modport Monitor ( + input aw_addr, aw_prot, aw_valid, aw_ready, + w_data, w_strb, w_valid, w_ready, + b_resp, b_valid, b_ready, + ar_addr, ar_prot, ar_valid, ar_ready, + r_data, r_resp, r_valid, r_ready + ); + endinterface @@ -499,6 +525,14 @@ interface AXI_LITE_DV #( output r_data, r_resp, r_valid, input r_ready ); + modport Monitor ( + input aw_addr, aw_prot, aw_valid, aw_ready, + w_data, w_strb, w_valid, w_ready, + b_resp, b_valid, b_ready, + ar_addr, ar_prot, ar_valid, ar_ready, + r_data, r_resp, r_valid, r_ready + ); + endinterface diff --git a/hw/vendor/pulp_platform_axi/src/axi_isolate.sv b/hw/vendor/pulp_platform_axi/src/axi_isolate.sv index 1eb484679..6385b2633 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_isolate.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_isolate.sv @@ -13,37 +13,168 @@ // - Wolfgang Roenninger // - Andreas Kurth -// Description: -// -// This module can isolate the AXI4+ATOPs bus on the master port from the slave port. When the -// isolation is not active, the two ports are directly connected. -// -// This module counts how many open transactions are currently in flight on the read and write -// channels. It is further capable of tracking the amount of open atomic transactions with read -// responses. -// -// The isolation interface has two signals: `isolate_i` and `isolated_o`. When `isolate_i` is -// asserted, all open transactions are gracefully terminated. When no transactions are in flight -// anymore, the `isolated_o` output is asserted. As long as `isolated_o` is asserted, all output -// signals in `mst_req_o` are silenced to `'0`. When isolated, new transactions initiated on the -// slave port are stalled until the isolation is terminated by deasserting `isolate_i`. - +`include "axi/typedef.svh" `include "common_cells/registers.svh" +/// This module can isolate the AXI4+ATOPs bus on the master port from the slave port. When the +/// isolation is not active, the two ports are directly connected. +/// +/// This module counts how many open transactions are currently in flight on the read and write +/// channels. It is further capable of tracking the amount of open atomic transactions with read +/// responses. +/// +/// The isolation interface has two signals: `isolate_i` and `isolated_o`. When `isolate_i` is +/// asserted, all open transactions are gracefully terminated. When no transactions are in flight +/// anymore, the `isolated_o` output is asserted. As long as `isolated_o` is asserted, all output +/// signals in `mst_req_o` are silenced to `'0`. When isolated, new transactions initiated on the +/// slave port are stalled until the isolation is terminated by deasserting `isolate_i`. +/// +/// ## Response +/// +/// If the `TerminateTransaction` parameter is set to `1'b1`, the module will return response errors +/// in case there is an incoming transaction while the module isolates. The data returned on the +/// bus is `1501A7ED` (hexspeak for isolated). +/// +/// If `TerminateTransaction` is set to `1'b0`, the transaction will block indefinitely until the +/// module is de-isolated again. module axi_isolate #( - parameter int unsigned NumPending = 32'd16, // Number of pending requests per channel - parameter type req_t = logic, // AXI request struct definition - parameter type resp_t = logic // AXI response struct definition + /// Maximum number of pending requests per channel + parameter int unsigned NumPending = 32'd16, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TerminateTransaction = 1'b0, + /// Support atomic operations (ATOPs) + parameter bit AtopSupport = 1'b1, + /// Address width of all AXI4+ATOP ports + parameter int signed AxiAddrWidth = 32'd0, + /// Data width of all AXI4+ATOP ports + parameter int signed AxiDataWidth = 32'd0, + /// ID width of all AXI4+ATOP ports + parameter int signed AxiIdWidth = 32'd0, + /// User signal width of all AXI4+ATOP ports + parameter int signed AxiUserWidth = 32'd0, + /// Request struct type of all AXI4+ATOP ports + parameter type axi_req_t = logic, + /// Response struct type of all AXI4+ATOP ports + parameter type axi_resp_t = logic ) ( - input logic clk_i, // clock - input logic rst_ni, // reset - input req_t slv_req_i, // slave port request struct - output resp_t slv_resp_o, // slave port response struct - output req_t mst_req_o, // master port request struct - input resp_t mst_resp_i, // master port response struct - input logic isolate_i, // isolate master port from slave port - output logic isolated_o // master port is isolated from slave port + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Slave port request + input axi_req_t slv_req_i, + /// Slave port response + output axi_resp_t slv_resp_o, + /// Master port request + output axi_req_t mst_req_o, + /// Master port response + input axi_resp_t mst_resp_i, + /// Isolate master port from slave port + input logic isolate_i, + /// Master port is isolated from slave port + output logic isolated_o ); + + typedef logic [AxiIdWidth-1:0] id_t; + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiDataWidth/8-1:0] strb_t; + typedef logic [AxiUserWidth-1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + + axi_req_t [1:0] demux_req; + axi_resp_t [1:0] demux_rsp; + + if (TerminateTransaction) begin + axi_demux #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport ( AtopSupport ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( NumPending ), + // We don't need many bits here as the common case will be to go for the pass-through. + .AxiLookBits ( 1 ), + .UniqueIds ( 1'b0 ), + .SpillAw ( 1'b0 ), + .SpillW ( 1'b0 ), + .SpillB ( 1'b0 ), + .SpillAr ( 1'b0 ), + .SpillR ( 1'b0 ) + ) i_axi_demux ( + .clk_i, + .rst_ni, + .test_i ( 1'b0 ), + .slv_req_i, + .slv_aw_select_i ( isolated_o ), + .slv_ar_select_i ( isolated_o ), + .slv_resp_o, + .mst_reqs_o ( demux_req ), + .mst_resps_i ( demux_rsp ) + ); + + axi_err_slv #( + .AxiIdWidth ( AxiIdWidth ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .RespData ( 'h1501A7ED ), + .ATOPs ( AtopSupport ), + .MaxTrans ( 1 ) + ) i_axi_err_slv ( + .clk_i, + .rst_ni, + .test_i ( 1'b0 ), + .slv_req_i ( demux_req[1] ), + .slv_resp_o ( demux_rsp[1] ) + ); + end else begin + assign demux_req[0] = slv_req_i; + assign slv_resp_o = demux_rsp[0]; + end + + axi_isolate_inner #( + .NumPending ( NumPending ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_isolate ( + .clk_i, + .rst_ni, + .slv_req_i ( demux_req[0] ), + .slv_resp_o ( demux_rsp[0] ), + .mst_req_o, + .mst_resp_i, + .isolate_i, + .isolated_o + ); +endmodule + +module axi_isolate_inner #( + parameter int unsigned NumPending = 32'd16, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i, + input logic isolate_i, + output logic isolated_o +); + // plus 1 in clog for accouning no open transaction, plus one bit for atomic injection localparam int unsigned CounterWidth = $clog2(NumPending + 32'd1) + 32'd1; typedef logic [CounterWidth-1:0] cnt_t; @@ -275,22 +406,26 @@ module axi_isolate #( // pragma translate_on endmodule -`include "axi/typedef.svh" `include "axi/assign.svh" +/// Interface variant of [`axi_isolate`](module.axi_isolate). +/// +/// See the documentation of the main module for the definition of ports and parameters. module axi_isolate_intf #( - parameter int unsigned NUM_PENDING = 32'd16, // Number of pending requests - parameter int unsigned AXI_ID_WIDTH = 32'd0, // AXI ID width - parameter int unsigned AXI_ADDR_WIDTH = 32'd0, // AXI address width - parameter int unsigned AXI_DATA_WIDTH = 32'd0, // AXI data width - parameter int unsigned AXI_USER_WIDTH = 32'd0 // AXI user width + parameter int unsigned NUM_PENDING = 32'd16, + parameter bit TERMINATE_TRANSACTION = 1'b0, + parameter bit ATOP_SUPPORT = 1'b1, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 ) ( - input logic clk_i, // clock - input logic rst_ni, // asynchronous reset active low - AXI_BUS.Slave slv, // slave port - AXI_BUS.Master mst, // master port - input logic isolate_i, // isolate master port from slave port - output logic isolated_o // master port is isolated from slave port + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave slv, + AXI_BUS.Master mst, + input logic isolate_i, + output logic isolated_o ); typedef logic [AXI_ID_WIDTH-1:0] id_t; typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; @@ -305,11 +440,11 @@ module axi_isolate_intf #( `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -318,18 +453,24 @@ module axi_isolate_intf #( `AXI_ASSIGN_TO_RESP(mst_resp, mst) axi_isolate #( - .NumPending ( NUM_PENDING ), // Number of pending requests per channel - .req_t ( req_t ), // AXI request struct definition - .resp_t ( resp_t ) // AXI response struct definition + .NumPending ( NUM_PENDING ), + .TerminateTransaction ( TERMINATE_TRANSACTION ), + .AtopSupport ( ATOP_SUPPORT ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .AxiIdWidth ( AXI_ID_WIDTH ), + .AxiUserWidth ( AXI_USER_WIDTH ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_isolate ( - .clk_i, // clock - .rst_ni, // reset - .slv_req_i ( slv_req ), // slave port request struct - .slv_resp_o ( slv_resp ), // slave port response struct - .mst_req_o ( mst_req ), // master port request struct - .mst_resp_i ( mst_resp ), // master port response struct - .isolate_i, // isolate master port from slave port - .isolated_o // master port is isolated from slave port + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_resp_o ( slv_resp ), + .mst_req_o ( mst_req ), + .mst_resp_i ( mst_resp ), + .isolate_i, + .isolated_o ); // pragma translate_off diff --git a/hw/vendor/pulp_platform_axi/src/axi_iw_converter.sv b/hw/vendor/pulp_platform_axi/src/axi_iw_converter.sv new file mode 100644 index 000000000..f29425eb1 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_iw_converter.sv @@ -0,0 +1,350 @@ +// Copyright (c) 2014-2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger + +`include "axi/typedef.svh" + +/// Convert between any two AXI ID widths. +/// +/// Any combination of slave and master port ID width is valid. When the master port ID width is +/// larger than or equal to the slave port ID width, slave port IDs are simply prepended with zeros +/// to the width of master port IDs. For *reducing* the ID width, i.e., when the master port ID +/// width is smaller than the slave port ID width, there are two options. +/// +/// ## Options for reducing the ID width +/// +/// The two options for reducing ID widths differ in the maximum number of different IDs that can be +/// in flight at the slave port of this module, given in the `AxiSlvPortMaxUniqIds` parameter. +/// +/// ### Fewer unique slave port IDs than master port IDs +/// +/// If `AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth`, there are fewer unique slave port IDs than +/// master port IDs. Therefore, IDs that are different at the slave port of this module can remain +/// different at the reduced-ID-width master port and thus remain *independently reorderable*. +/// Since the IDs are master port are nonetheless shorter than at the slave port, they need to be +/// *remapped*. An instance of [`axi_id_remap`](module.axi_id_remap) handles this case. +/// +/// ### More unique slave port IDs than master port IDs +/// +/// If `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`, there are more unique slave port IDs than +/// master port IDs. Therefore, some IDs that are different at the slave port need to be assigned +/// to the same master port ID and thus become ordered with respect to each other. An instance of +/// [`axi_id_serialize`](module.axi_id_serialize) handles this case. +module axi_iw_converter #( + /// ID width of the AXI4+ATOP slave port + parameter int unsigned AxiSlvPortIdWidth = 32'd0, + /// ID width of the AXI4+ATOP master port + parameter int unsigned AxiMstPortIdWidth = 32'd0, + /// Maximum number of different IDs that can be in flight at the slave port. Reads and writes are + /// counted separately (except for ATOPs, which count as both read and write). + /// + /// It is legal for upstream to have transactions with more unique IDs than the maximum given by + /// this parameter in flight, but a transaction exceeding the maximum will be stalled until all + /// transactions of another ID complete. + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the slave port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to [`axi_id_remap` as `AxiMaxTxnsPerId` + /// parameter](module.axi_id_remap#parameter.AxiMaxTxnsPerId). + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + /// Maximum number of in-flight transactions at the slave port. Reads and writes are counted + /// separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiSlvPortMaxTxns). + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + /// Maximum number of different IDs that can be in flight at the master port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxUniqIds). + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the master port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxTxnsPerId). + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + /// Address width of both AXI4+ATOP ports + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width of both AXI4+ATOP ports + parameter int unsigned AxiDataWidth = 32'd0, + /// User signal width of both AXI4+ATOP ports + parameter int unsigned AxiUserWidth = 32'd0, + /// Request struct type of the AXI4+ATOP slave port + parameter type slv_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type slv_resp_t = logic, + /// Request struct type of the AXI4+ATOP master port + parameter type mst_req_t = logic, + /// Response struct type of the AXI4+ATOP master port + parameter type mst_resp_t = logic +) ( + /// Rising-edge clock of both ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Slave port request + input slv_req_t slv_req_i, + /// Slave port response + output slv_resp_t slv_resp_o, + /// Master port request + output mst_req_t mst_req_o, + /// Master port response + input mst_resp_t mst_resp_i +); + + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiSlvPortIdWidth-1:0] slv_id_t; + typedef logic [AxiMstPortIdWidth-1:0] mst_id_t; + typedef logic [AxiDataWidth/8-1:0] strb_t; + typedef logic [AxiUserWidth-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_t, user_t) + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(mst_b_t, mst_id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t) + + if (AxiMstPortIdWidth < AxiSlvPortIdWidth) begin : gen_downsize + if (AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth) begin : gen_remap + axi_id_remap #( + .AxiSlvPortIdWidth ( AxiSlvPortIdWidth ), + .AxiMstPortIdWidth ( AxiMstPortIdWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ) + ) i_axi_id_remap ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req_i ), + .slv_resp_o ( slv_resp_o ), + .mst_req_o ( mst_req_o ), + .mst_resp_i ( mst_resp_i ) + ); + end else begin : gen_serialize + axi_id_serialize #( + .AxiSlvPortIdWidth ( AxiSlvPortIdWidth ), + .AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ), + .AxiMstPortIdWidth ( AxiMstPortIdWidth ), + .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), + .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiUserWidth ( AxiUserWidth ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ) + ) i_axi_id_serialize ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req_i ), + .slv_resp_o ( slv_resp_o ), + .mst_req_o ( mst_req_o ), + .mst_resp_i ( mst_resp_i ) + ); + end + end else if (AxiMstPortIdWidth > AxiSlvPortIdWidth) begin : gen_upsize + axi_id_prepend #( + .NoBus ( 32'd1 ), + .AxiIdWidthSlvPort ( AxiSlvPortIdWidth ), + .AxiIdWidthMstPort ( AxiMstPortIdWidth ), + .slv_aw_chan_t ( slv_aw_t ), + .slv_w_chan_t ( w_t ), + .slv_b_chan_t ( slv_b_t ), + .slv_ar_chan_t ( slv_ar_t ), + .slv_r_chan_t ( slv_r_t ), + .mst_aw_chan_t ( mst_aw_t ), + .mst_w_chan_t ( w_t ), + .mst_b_chan_t ( mst_b_t ), + .mst_ar_chan_t ( mst_ar_t ), + .mst_r_chan_t ( mst_r_t ) + ) i_axi_id_prepend ( + .pre_id_i ( '0 ), + .slv_aw_chans_i ( slv_req_i.aw ), + .slv_aw_valids_i ( slv_req_i.aw_valid ), + .slv_aw_readies_o ( slv_resp_o.aw_ready ), + .slv_w_chans_i ( slv_req_i.w ), + .slv_w_valids_i ( slv_req_i.w_valid ), + .slv_w_readies_o ( slv_resp_o.w_ready ), + .slv_b_chans_o ( slv_resp_o.b ), + .slv_b_valids_o ( slv_resp_o.b_valid ), + .slv_b_readies_i ( slv_req_i.b_ready ), + .slv_ar_chans_i ( slv_req_i.ar ), + .slv_ar_valids_i ( slv_req_i.ar_valid ), + .slv_ar_readies_o ( slv_resp_o.ar_ready ), + .slv_r_chans_o ( slv_resp_o.r ), + .slv_r_valids_o ( slv_resp_o.r_valid ), + .slv_r_readies_i ( slv_req_i.r_ready ), + .mst_aw_chans_o ( mst_req_o.aw ), + .mst_aw_valids_o ( mst_req_o.aw_valid ), + .mst_aw_readies_i ( mst_resp_i.aw_ready ), + .mst_w_chans_o ( mst_req_o.w ), + .mst_w_valids_o ( mst_req_o.w_valid ), + .mst_w_readies_i ( mst_resp_i.w_ready ), + .mst_b_chans_i ( mst_resp_i.b ), + .mst_b_valids_i ( mst_resp_i.b_valid ), + .mst_b_readies_o ( mst_req_o.b_ready ), + .mst_ar_chans_o ( mst_req_o.ar ), + .mst_ar_valids_o ( mst_req_o.ar_valid ), + .mst_ar_readies_i ( mst_resp_i.ar_ready ), + .mst_r_chans_i ( mst_resp_i.r ), + .mst_r_valids_i ( mst_resp_i.r_valid ), + .mst_r_readies_o ( mst_req_o.r_ready ) + ); + end else begin : gen_passthrough + assign mst_req_o = slv_req_i; + assign slv_resp_o = mst_resp_i; + end + + // pragma translate_off + `ifndef VERILATOR + initial begin : p_assert + assert(AxiAddrWidth > 32'd0) + else $fatal(1, "Parameter AxiAddrWidth has to be larger than 0!"); + assert(AxiDataWidth > 32'd0) + else $fatal(1, "Parameter AxiDataWidth has to be larger than 0!"); + assert(AxiUserWidth > 32'd0) + else $fatal(1, "Parameter AxiUserWidth has to be larger than 0!"); + assert(AxiSlvPortIdWidth > 32'd0) + else $fatal(1, "Parameter AxiSlvPortIdWidth has to be larger than 0!"); + assert(AxiMstPortIdWidth > 32'd0) + else $fatal(1, "Parameter AxiMstPortIdWidth has to be larger than 0!"); + if (AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth) begin + assert(AxiSlvPortMaxTxnsPerId > 32'd0) + else $fatal(1, "Parameter AxiSlvPortMaxTxnsPerId has to be larger than 0!"); + end else begin + assert(AxiMstPortMaxUniqIds > 32'd0) + else $fatal(1, "Parameter AxiMstPortMaxUniqIds has to be larger than 0!"); + assert(AxiMstPortMaxTxnsPerId > 32'd0) + else $fatal(1, "Parameter AxiMstPortMaxTxnsPerId has to be larger than 0!"); + end + assert($bits(slv_req_i.aw.addr) == $bits(mst_req_o.aw.addr)) + else $fatal(1, "AXI AW address widths are not equal!"); + assert($bits(slv_req_i.w.data) == $bits(mst_req_o.w.data)) + else $fatal(1, "AXI W data widths are not equal!"); + assert($bits(slv_req_i.ar.addr) == $bits(mst_req_o.ar.addr)) + else $fatal(1, "AXI AR address widths are not equal!"); + assert($bits(slv_resp_o.r.data) == $bits(mst_resp_i.r.data)) + else $fatal(1, "AXI R data widths are not equal!"); + end + `endif + // pragma translate_on +endmodule + + +`include "axi/assign.svh" + +/// Interface variant of [`axi_iw_converter`](module.axi_iw_converter). +/// +/// See the documentation of the main module for the definition of ports and parameters. +module axi_iw_converter_intf #( + parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0, + parameter int unsigned AXI_MST_PORT_ID_WIDTH = 32'd0, + parameter int unsigned AXI_SLV_PORT_MAX_UNIQ_IDS = 32'd0, + parameter int unsigned AXI_SLV_PORT_MAX_TXNS_PER_ID = 32'd0, + parameter int unsigned AXI_SLV_PORT_MAX_TXNS = 32'd0, + parameter int unsigned AXI_MST_PORT_MAX_UNIQ_IDS = 32'd0, + parameter int unsigned AXI_MST_PORT_MAX_TXNS_PER_ID = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave slv, + AXI_BUS.Master mst +); + typedef logic [AXI_SLV_PORT_ID_WIDTH-1:0] slv_id_t; + typedef logic [AXI_MST_PORT_ID_WIDTH-1:0] mst_id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t; + typedef logic [AXI_USER_WIDTH-1:0] axi_user_t; + + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, axi_addr_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_W_CHAN_T(slv_w_chan_t, axi_data_t, axi_strb_t, axi_user_t) + `AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, axi_addr_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, axi_data_t, slv_id_t, axi_user_t) + `AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, slv_w_chan_t, slv_ar_chan_t) + `AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t) + + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, axi_addr_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_W_CHAN_T(mst_w_chan_t, axi_data_t, axi_strb_t, axi_user_t) + `AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, axi_addr_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, axi_data_t, mst_id_t, axi_user_t) + `AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, mst_w_chan_t, mst_ar_chan_t) + `AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t) + + slv_req_t slv_req; + slv_resp_t slv_resp; + mst_req_t mst_req; + mst_resp_t mst_resp; + + `AXI_ASSIGN_TO_REQ(slv_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, slv_resp) + `AXI_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_ASSIGN_TO_RESP(mst_resp, mst) + + axi_iw_converter #( + .AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ), + .AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ), + .AxiSlvPortMaxUniqIds ( AXI_SLV_PORT_MAX_UNIQ_IDS ), + .AxiSlvPortMaxTxnsPerId ( AXI_SLV_PORT_MAX_TXNS_PER_ID ), + .AxiSlvPortMaxTxns ( AXI_SLV_PORT_MAX_TXNS ), + .AxiMstPortMaxUniqIds ( AXI_MST_PORT_MAX_UNIQ_IDS ), + .AxiMstPortMaxTxnsPerId ( AXI_MST_PORT_MAX_TXNS_PER_ID ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .AxiUserWidth ( AXI_USER_WIDTH ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ) + ) i_axi_iw_converter ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_resp_o ( slv_resp ), + .mst_req_o ( mst_req ), + .mst_resp_i ( mst_resp ) + ); + // pragma translate_off + `ifndef VERILATOR + initial begin + assert (slv.AXI_ID_WIDTH == AXI_SLV_PORT_ID_WIDTH); + assert (slv.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH); + assert (slv.AXI_DATA_WIDTH == AXI_DATA_WIDTH); + assert (slv.AXI_USER_WIDTH == AXI_USER_WIDTH); + assert (mst.AXI_ID_WIDTH == AXI_MST_PORT_ID_WIDTH); + assert (mst.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH); + assert (mst.AXI_DATA_WIDTH == AXI_DATA_WIDTH); + assert (mst.AXI_USER_WIDTH == AXI_USER_WIDTH); + end + `endif + // pragma translate_on +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_lfsr.sv b/hw/vendor/pulp_platform_axi/src/axi_lfsr.sv new file mode 100644 index 000000000..2085a76fe --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_lfsr.sv @@ -0,0 +1,121 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +`include "axi/typedef.svh" + +/// AXI4 LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lfsr #( + /// AXI4 Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Addr Width + parameter int unsigned AddrWidth = 32'd0, + /// AXI4 Id Width + parameter int unsigned IdWidth = 32'd0, + /// AXI4 User Width + parameter int unsigned UserWidth = 32'd0, + /// AXI4 request struct definition + parameter type axi_req_t = logic, + /// AXI4 response struct definition + parameter type axi_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 request struct + input axi_req_t req_i, + /// AXI4 response struct + output axi_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + /// Address Type + typedef logic [AddrWidth-1:0] addr_t; + /// Data type + typedef logic [DataWidth-1:0] data_t; + /// Strobe Type + typedef logic [StrbWidth-1:0] strb_t; + + // AXI Lite typedef + `AXI_LITE_TYPEDEF_AW_CHAN_T(axi_lite_aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(axi_lite_w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(axi_lite_b_chan_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(axi_lite_ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(axi_lite_r_chan_t, data_t) + + `AXI_LITE_TYPEDEF_REQ_T(axi_lite_req_t, axi_lite_aw_chan_t, axi_lite_w_chan_t, axi_lite_ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_lite_rsp_t, axi_lite_b_chan_t, axi_lite_r_chan_t) + + // AXI Lite buses + axi_lite_req_t axi_lite_req; + axi_lite_rsp_t axi_lite_rsp; + + axi_to_axi_lite #( + .AxiAddrWidth ( AddrWidth ), + .AxiDataWidth ( DataWidth ), + .AxiIdWidth ( IdWidth ), + .AxiUserWidth ( UserWidth ), + .AxiMaxWriteTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .AxiMaxReadTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .FallThrough ( 1'b0 ), + .full_req_t ( axi_req_t ), + .full_resp_t ( axi_rsp_t ), + .lite_req_t ( axi_lite_req_t ), + .lite_resp_t ( axi_lite_rsp_t ) + ) i_axi_to_axi_lite ( + .clk_i, + .rst_ni, + .test_i ( testmode_i ), + .slv_req_i ( req_i ), + .slv_resp_o ( rsp_o ), + .mst_req_o ( axi_lite_req ), + .mst_resp_i ( axi_lite_rsp ) + ); + + axi_lite_lfsr #( + .DataWidth ( DataWidth ), + .axi_lite_req_t ( axi_lite_req_t ), + .axi_lite_rsp_t ( axi_lite_rsp_t ) + ) i_axi_lite_lfsr ( + .clk_i, + .rst_ni, + .testmode_i, + .w_ser_data_i, + .w_ser_data_o, + .w_ser_en_i, + .r_ser_data_i, + .r_ser_data_o, + .r_ser_en_i, + .req_i ( axi_lite_req ), + .rsp_o ( axi_lite_rsp ) + ); + +endmodule : axi_lfsr diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_demux.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_demux.sv index 736b3018b..5513fb443 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_lite_demux.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_demux.sv @@ -14,6 +14,12 @@ `include "common_cells/registers.svh" +`ifdef QUESTA +// Derive `TARGET_VSIM`, which is used for tool-specific workarounds in this file, from `QUESTA`, +// which is automatically set in Questa. +`define TARGET_VSIM +`endif + // axi_lite_demux: Demultiplex an AXI4-Lite bus from one slave port to multiple master ports. // The selection signal at the AW and AR channel has to follow the same // stability rules as the corresponding AXI4-Lite channel. @@ -24,8 +30,8 @@ module axi_lite_demux #( parameter type b_chan_t = logic, // AXI4-Lite B channel parameter type ar_chan_t = logic, // AXI4-Lite AR channel parameter type r_chan_t = logic, // AXI4-Lite R channel - parameter type req_t = logic, // AXI4-Lite request struct - parameter type resp_t = logic, // AXI4-Lite response struct + parameter type axi_req_t = logic, // AXI4-Lite request struct + parameter type axi_resp_t = logic, // AXI4-Lite response struct parameter int unsigned NoMstPorts = 32'd0, // Number of instantiated ports parameter int unsigned MaxTrans = 32'd0, // Maximum number of open transactions per channel parameter bit FallThrough = 1'b0, // FIFOs are in fall through mode @@ -37,17 +43,17 @@ module axi_lite_demux #( // Dependent parameters, DO NOT OVERRIDE! parameter type select_t = logic [$clog2(NoMstPorts)-1:0] ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, + input logic clk_i, + input logic rst_ni, + input logic test_i, // slave port (AXI4-Lite input), connect master module here - input req_t slv_req_i, - input select_t slv_aw_select_i, - input select_t slv_ar_select_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + input select_t slv_aw_select_i, + input select_t slv_ar_select_i, + output axi_resp_t slv_resp_o, // master ports (AXI4-Lite outputs), connect slave modules here - output req_t [NoMstPorts-1:0] mst_reqs_o, - input resp_t [NoMstPorts-1:0] mst_resps_i + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); //-------------------------------------- @@ -64,9 +70,72 @@ module axi_lite_demux #( if (NoMstPorts == 32'd1) begin : gen_no_demux // degenerate case, connect slave to master port - // AW channel - assign mst_reqs_o[0] = slv_req_i; - assign slv_resp_o = mst_resps_i[0]; + spill_register #( + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_resp_o.aw_ready ), + .data_i ( slv_req_i.aw ), + .valid_o ( mst_reqs_o[0].aw_valid ), + .ready_i ( mst_resps_i[0].aw_ready ), + .data_o ( mst_reqs_o[0].aw ) + ); + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_resp_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( mst_reqs_o[0].w_valid ), + .ready_i ( mst_resps_i[0].w_ready ), + .data_o ( mst_reqs_o[0].w ) + ); + spill_register #( + .T ( b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resps_i[0].b_valid ), + .ready_o ( mst_reqs_o[0].b_ready ), + .data_i ( mst_resps_i[0].b ), + .valid_o ( slv_resp_o.b_valid ), + .ready_i ( slv_req_i.b_ready ), + .data_o ( slv_resp_o.b ) + ); + spill_register #( + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_resp_o.ar_ready ), + .data_i ( slv_req_i.ar ), + .valid_o ( mst_reqs_o[0].ar_valid ), + .ready_i ( mst_resps_i[0].ar_ready ), + .data_o ( mst_reqs_o[0].ar ) + ); + spill_register #( + .T ( r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resps_i[0].r_valid ), + .ready_o ( mst_reqs_o[0].r_ready ), + .data_i ( mst_resps_i[0].r ), + .valid_o ( slv_resp_o.r_valid ), + .ready_i ( slv_req_i.r_ready ), + .data_o ( slv_resp_o.r ) + ); + end else begin : gen_demux // normal non degenerate case @@ -123,22 +192,32 @@ module axi_lite_demux #( //-------------------------------------- // AW Channel //-------------------------------------- - aw_chan_select_t slv_aw_inp; - assign slv_aw_inp.aw = slv_req_i.aw; - assign slv_aw_inp.select = slv_aw_select_i; + `ifdef TARGET_VSIM + // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before + // instantiating `spill_register`. + typedef logic [$bits(aw_chan_select_t)-1:0] aw_chan_select_flat_t; + `else + // Other tools, such as VCS, have problems with `$bits()`, so the workaround cannot be used + // generally. + typedef aw_chan_select_t aw_chan_select_flat_t; + `endif + aw_chan_select_flat_t slv_aw_chan_select_in_flat, + slv_aw_chan_select_out_flat; + assign slv_aw_chan_select_in_flat = {slv_req_i.aw, slv_aw_select_i}; spill_register #( - .T ( aw_chan_select_t ), - .Bypass ( ~SpillAw ) + .T ( aw_chan_select_flat_t ), + .Bypass ( ~SpillAw ) ) i_aw_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_resp_o.aw_ready ), - .data_i ( slv_aw_inp ), - .valid_o ( slv_aw_valid ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_resp_o.aw_ready ), + .data_i ( slv_aw_chan_select_in_flat ), + .valid_o ( slv_aw_valid ), + .ready_i ( slv_aw_ready ), + .data_o ( slv_aw_chan_select_out_flat ) ); + assign slv_aw_chan = slv_aw_chan_select_out_flat; // replicate AW channel to the request output for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_aw @@ -278,22 +357,29 @@ module axi_lite_demux #( //-------------------------------------- // AR Channel //-------------------------------------- - ar_chan_select_t slv_ar_inp; - assign slv_ar_inp.ar = slv_req_i.ar; - assign slv_ar_inp.select = slv_ar_select_i; + // Workaround for bug in Questa (see comments on AW channel for details). + `ifdef TARGET_VSIM + typedef logic [$bits(ar_chan_select_t)-1:0] ar_chan_select_flat_t; + `else + typedef ar_chan_select_t ar_chan_select_flat_t; + `endif + ar_chan_select_flat_t slv_ar_chan_select_in_flat, + slv_ar_chan_select_out_flat; + assign slv_ar_chan_select_in_flat = {slv_req_i.ar, slv_ar_select_i}; spill_register #( - .T ( ar_chan_select_t ), - .Bypass ( ~SpillAr ) + .T ( ar_chan_select_flat_t ), + .Bypass ( ~SpillAr ) ) i_ar_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_resp_o.ar_ready ), - .data_i ( slv_ar_inp ), - .valid_o ( slv_ar_valid ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_resp_o.ar_ready ), + .data_i ( slv_ar_chan_select_in_flat ), + .valid_o ( slv_ar_valid ), + .ready_i ( slv_ar_ready ), + .data_o ( slv_ar_chan_select_out_flat ) ); + assign slv_ar_chan = slv_ar_chan_select_out_flat; // replicate AR channel for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_ar @@ -340,8 +426,15 @@ module axi_lite_demux #( ); // connect the response if the FIFO has valid data in it - assign slv_r_chan = (!r_fifo_empty) ? mst_resps_i[r_select].r : '0; - assign slv_r_valid = ~r_fifo_empty & mst_resps_i[r_select].r_valid; + always_comb begin + slv_r_chan = '0; + slv_r_valid = '0; + if (!r_fifo_empty) begin + slv_r_chan = mst_resps_i[r_select].r; + slv_r_valid = mst_resps_i[r_select].r_valid; + end + end + for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_r assign mst_reqs_o[i].r_ready = ~r_fifo_empty & slv_r_ready & (r_select == select_t'(i)); end @@ -417,13 +510,13 @@ module axi_lite_demux_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req; - resp_t slv_resp; - req_t [NoMstPorts-1:0] mst_reqs; - resp_t [NoMstPorts-1:0] mst_resps; + axi_req_t slv_req; + axi_resp_t slv_resp; + axi_req_t [NoMstPorts-1:0] mst_reqs; + axi_resp_t [NoMstPorts-1:0] mst_resps; `AXI_LITE_ASSIGN_TO_REQ(slv_req, slv) `AXI_LITE_ASSIGN_FROM_RESP(slv, slv_resp) @@ -434,13 +527,13 @@ module axi_lite_demux_intf #( end axi_lite_demux #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoMstPorts ( NoMstPorts ), .MaxTrans ( MaxTrans ), .FallThrough ( FallThrough ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_dw_converter.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_dw_converter.sv new file mode 100644 index 000000000..d8055481f --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_dw_converter.sv @@ -0,0 +1,567 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Wolfgang Roenninger + +/// # AXI4-Lite data width downsize module. +/// +/// ## Down Conversion +/// +/// The module will be in this mode if `AxiSlvPortDataWidth > AxiMstPortDataWidth`. +/// The module does multiple transactions on the master port for each transaction of the salve port. +/// +/// The address on the master port will be aligned to the bus width, regardless what the input +/// address was. The number of transactions generated on the master port is equal to the +/// `DownsizeFactor = AxiSlvPortDataWidth / AxiMstPortDataWidth`. +/// +/// Eg: `AxiAddrWidth == 32'd16`, `AxiSlvPortDataWidth == 32'64`, `AxiMstPortDataWidth == 32'd32' +/// This is for write transactions. Reads are accessing the whole width of the slave port. +/// +/// +/// | EG NUM | SLV ADDR | SLV W DATA | SLV W STRB | MST ADDR | MST W DATA | MST W STRB | +/// |--------|----------|--------------------|------------|----------|------------|------------| +/// | 1 | 0x0000 | 0xBEEFBEEFAAAABBBB | 0xAB | 0x0000 | 0xAAAABBBB | 0xB | +/// | 1 | | | | 0x0004 | 0xBEEFBEEF | 0xA | +/// | | | | | | | | +/// | 2 | 0x0000 | 0xBEEFBEEFAAAABBBB | 0xF0 | 0x0000 | 0xAAAABBBB | 0x0 | +/// | 2 | | | | 0x0004 | 0xBEEFBEEF | 0xF | +/// | | | | | | | | +/// | 3 | 0x0004 | 0xBEEFBEEFAAAABBBB | 0xF0 | 0x0000 | 0xAAAABBBB | 0x0 | +/// | 3 | | | | 0x0004 | 0xBEEFBEEF | 0xF | +/// | | | | | | | | +/// | 4 | 0x0004 | 0xBEEFBEEFAAAABBBB | 0x0F | 0x0000 | 0xAAAABBBB | 0xF | +/// | 4 | | | | 0x0004 | 0xBEEFBEEF | 0x0 | +/// | | | | | | | | +/// | 5 | 0x0005 | 0xBEEFBE0000000000 | 0xE0 | 0x0000 | 0x00000000 | 0x0 | +/// | 5 | | | | 0x0004 | 0xBEEFBE00 | 0xE | +/// +/// Response field is aggregated (OR'ed) between the multiple requests made on the master port. +/// If one of the requests on the master port errors, the error response of the request +/// on the slave port will also signal an error. +/// +/// ## Up conversion +/// +/// The module will be in this mode if `AxiSlvPortDataWidth < AxiMstPortDataWidth`. +/// This mode will generate the same amount of transactions on the master port as on the slave port. +/// Data is replicated to match the bus width. Write strobes are silenced for the byte lanes not +/// written. +/// +/// ## Pass Through +/// +/// The module will be in this mode if `AxiSlvPortDataWidth == AxiMstPortDataWidth`. +/// Here the module passes through the slave port to the master port. +`include "common_cells/registers.svh" +module axi_lite_dw_converter #( + /// AXI4-Lite address width of the ports. + parameter int unsigned AxiAddrWidth = 32'd0, + /// AXI4-Lite data width of the slave port. + parameter int unsigned AxiSlvPortDataWidth = 32'd0, + /// AXI4-Lite data width of the master port. + parameter int unsigned AxiMstPortDataWidth = 32'd0, + /// AXI4-Lite AW channel struct type. This is for both ports the same. + parameter type axi_lite_aw_t = logic, + /// AXI4-Lite W channel struct type of the slave port. + parameter type axi_lite_slv_w_t = logic, + /// AXI4-Lite W channel struct type of the master port. + parameter type axi_lite_mst_w_t = logic, + /// AXI4-Lite B channel struct type. This is for both ports. + parameter type axi_lite_b_t = logic, + /// AXI4-Lite AR channel struct type. This is for both ports. + parameter type axi_lite_ar_t = logic, + /// AXI4-Lite R channel struct type of the slave port. + parameter type axi_lite_slv_r_t = logic, + /// AXI4-Lite R channel struct type of the master port. + parameter type axi_lite_mst_r_t = logic, + /// AXI4-Lite request struct of the slave port. + parameter type axi_lite_slv_req_t = logic, + /// AXI4-Lite response struct of the slave port. + parameter type axi_lite_slv_res_t = logic, + /// AXI4-Lite request struct of the master port. + parameter type axi_lite_mst_req_t = logic, + /// AXI4-Lite response struct of the master port. + parameter type axi_lite_mst_res_t = logic +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchrounous reset, active low. + input logic rst_ni, + /// Salve port, AXI4-Lite request. + input axi_lite_slv_req_t slv_req_i, + /// Salve port, AXI4-Lite response. + output axi_lite_slv_res_t slv_res_o, + /// Master port, AXI4-Lite request. + output axi_lite_mst_req_t mst_req_o, + /// Master port, AXI4-Lite response. + input axi_lite_mst_res_t mst_res_i +); + // Strobe parameter for the two AXI4-Lite ports. + localparam int unsigned AxiSlvPortStrbWidth = AxiSlvPortDataWidth / 32'd8; + localparam int unsigned AxiMstPortStrbWidth = AxiMstPortDataWidth / 32'd8; + typedef logic [AxiAddrWidth-1:0] addr_t; + + // AXI4-Lite downsizer + if (AxiSlvPortDataWidth > AxiMstPortDataWidth) begin : gen_downsizer + // The Downsize factor determines how often the data channel has to be multiplexed. + localparam int unsigned DownsizeFactor = AxiSlvPortDataWidth / AxiMstPortDataWidth; + // Selection width for choosing the byte lanes. + localparam int unsigned SelWidth = $clog2(DownsizeFactor); + // Type for the selection signal. + typedef logic [SelWidth-1:0] sel_t; + // Offset determines, which part of the address corresponds to the `w_chan_sel` signal. + localparam int unsigned SelOffset = $clog2(AxiMstPortStrbWidth); + + // Calculate the output address for the master port. + // `address`: The address as seen on the salve port. + // `sel`: T The current selection. + // `l_zero`: If set, the lowest bits are zero, for all generated addresses after the first. + function automatic addr_t out_address(input addr_t address, input sel_t sel); + out_address = address; + out_address[SelOffset+:SelWidth] = sel; + out_address[SelOffset-1:0] = SelOffset'(0); + endfunction : out_address + + // Write channels. + // Input spill register of the AW channel. + axi_lite_aw_t aw_chan_spill; + logic aw_chan_spill_valid, aw_chan_spill_ready; + + spill_register #( + .T ( axi_lite_aw_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_aw ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_res_o.aw_ready ), + .data_i ( slv_req_i.aw ), + .valid_o ( aw_chan_spill_valid ), + .ready_i ( aw_chan_spill_ready ), + .data_o ( aw_chan_spill ) + ); + + sel_t aw_sel_q, aw_sel_d; + logic aw_sel_load; + // AW channel output assignment + always_comb begin : proc_aw_chan_oup + mst_req_o.aw = aw_chan_spill; + mst_req_o.aw.addr = out_address(aw_chan_spill.addr, aw_sel_q); + end + // Slave port aw is valid, if there is something in the spill register. + assign mst_req_o.aw_valid = aw_chan_spill_valid; + assign aw_chan_spill_ready = mst_res_i.aw_ready & (&aw_sel_q); + + assign aw_sel_load = mst_req_o.aw_valid & mst_res_i.aw_ready; + assign aw_sel_d = sel_t'(aw_sel_q + 1'b1); + `FFLARN(aw_sel_q, aw_sel_d, aw_sel_load, '0, clk_i, rst_ni) + + // Input spill register of the W channel. + axi_lite_slv_w_t w_chan_spill; + logic w_chan_spill_valid, w_chan_spill_ready; + spill_register #( + .T ( axi_lite_slv_w_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_w ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_res_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( w_chan_spill_valid ), + .ready_i ( w_chan_spill_ready ), + .data_o ( w_chan_spill ) + ); + + // Data multiplexer on the W channel + sel_t w_sel_q, w_sel_d; + logic w_sel_load; + // W channel output assignment + assign mst_req_o.w = axi_lite_mst_w_t'{ + data: w_chan_spill.data[w_sel_q*AxiMstPortDataWidth+:AxiMstPortDataWidth], + strb: w_chan_spill.strb[w_sel_q*AxiMstPortStrbWidth+:AxiMstPortStrbWidth], + default: '0 + }; + assign mst_req_o.w_valid = w_chan_spill_valid; + assign w_chan_spill_ready = mst_res_i.w_ready & (&w_sel_q); + + assign w_sel_load = mst_req_o.w_valid & mst_res_i.w_ready; + assign w_sel_d = sel_t'(w_sel_q + 1'b1); + `FFLARN(w_sel_q, w_sel_d, w_sel_load, '0, clk_i, rst_ni) + + // B response aggregation + // Slave port B output is the aggregated error of the last few B responses. + sel_t b_sel_q, b_sel_d; + axi_pkg::resp_t b_resp_q, b_resp_d; + logic b_resp_load; + + assign slv_res_o.b = axi_lite_b_t'{ + resp: b_resp_q | mst_res_i.b.resp, + default: '0 + }; + // Output is valid, if it is the last b response for the wide W, we have something + // in the B FIFO and the B response is valid from the master port. + assign slv_res_o.b_valid = mst_res_i.b_valid & (&b_sel_q); + + // Assign the b_channel ready output. The master port is ready if something is in the + // B FIFO. Except, if it is the last one which should do a response on the slave port. + assign mst_req_o.b_ready = (&b_sel_q) ? slv_req_i.b_ready : 1'b1; + // B channel error response retention FF + assign b_sel_d = sel_t'(b_sel_q + 1'b1); + assign b_resp_d = (&b_sel_q) ? axi_pkg::RESP_OKAY : (b_resp_q | mst_res_i.b.resp); + assign b_resp_load = mst_res_i.b_valid & mst_req_o.b_ready; + `FFLARN(b_sel_q, b_sel_d, b_resp_load, '0, clk_i, rst_ni) + `FFLARN(b_resp_q, b_resp_d, b_resp_load, axi_pkg::RESP_OKAY, clk_i, rst_ni) + + // Read channels. + // Input spill register of the AW channel. + axi_lite_ar_t ar_chan_spill; + logic ar_chan_spill_valid, ar_chan_spill_ready; + + spill_register #( + .T ( axi_lite_ar_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_ar ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_res_o.ar_ready ), + .data_i ( slv_req_i.ar ), + .valid_o ( ar_chan_spill_valid ), + .ready_i ( ar_chan_spill_ready ), + .data_o ( ar_chan_spill ) + ); + + sel_t ar_sel_q, ar_sel_d; + logic ar_sel_load; + // AR channel output assignment + always_comb begin : proc_ar_chan_oup + mst_req_o.ar = ar_chan_spill; + mst_req_o.ar.addr = out_address(ar_chan_spill.addr, ar_sel_q); + end + // Slave port aw is valid, if there is something in the spill register. + assign mst_req_o.ar_valid = ar_chan_spill_valid; + assign ar_chan_spill_ready = mst_res_i.ar_ready & (&ar_sel_q); + + assign ar_sel_load = mst_req_o.ar_valid & mst_res_i.ar_ready; + assign ar_sel_d = sel_t'(ar_sel_q + 1'b1); + `FFLARN(ar_sel_q, ar_sel_d, ar_sel_load, '0, clk_i, rst_ni) + + // Responses have to be aggregated, one FF less, as the last data is feed directly through. + sel_t r_sel_q, r_sel_d; + logic r_sel_load; + axi_lite_mst_r_t [DownsizeFactor-2:0] r_chan_mst_q; + logic [DownsizeFactor-2:0] r_chan_mst_load; + for (genvar i = 0; unsigned'(i) < (DownsizeFactor-1); i++) begin : gen_r_chan_ff + assign r_chan_mst_load[i] = (sel_t'(i) == r_sel_q) & mst_res_i.r_valid & mst_req_o.r_ready; + `FFLARN(r_chan_mst_q[i], mst_res_i.r, r_chan_mst_load[i], axi_lite_mst_r_t'{default: '0}, clk_i, rst_ni) + end + assign r_sel_load = mst_res_i.r_valid & mst_req_o.r_ready; + assign r_sel_d = sel_t'(r_sel_q + 1'b1); + `FFLARN(r_sel_q, r_sel_d, r_sel_load, '0, clk_i, rst_ni) + + always_comb begin : proc_r_chan_oup + slv_res_o.r = axi_lite_slv_r_t'{ + resp: mst_res_i.r.resp, + default: '0 + }; + // Response is the OR of all responses + for (int unsigned i = 0; i < (DownsizeFactor-1); i++) begin + slv_res_o.r.resp = slv_res_o.r.resp | r_chan_mst_q[i].resp; + slv_res_o.r.data[i*AxiMstPortDataWidth+:AxiMstPortDataWidth] = r_chan_mst_q[i].data; + end + // The highest bits of the data can be directly the master port. + slv_res_o.r.data[(DownsizeFactor-1)*AxiMstPortDataWidth+:AxiMstPortDataWidth] = + mst_res_i.r.data; + end + + assign slv_res_o.r_valid = (&r_sel_q) ? mst_res_i.r_valid : 1'b0; + assign mst_req_o.r_ready = (&r_sel_q) ? slv_req_i.r_ready : 1'b1; + + end else if (AxiMstPortDataWidth > AxiSlvPortDataWidth) begin : gen_upsizer + // The upsize factor determines the amount of replication. + localparam int unsigned UpsizeFactor = AxiMstPortDataWidth / AxiSlvPortDataWidth; + + // Selection type and offset for the address + localparam int unsigned SelOffset = $clog2(AxiSlvPortStrbWidth); + localparam int unsigned SelWidth = $clog2(UpsizeFactor); + typedef logic [SelWidth-1:0] sel_t; + + // AW channel can be passed through, however block handshake if FIFO is full. + assign mst_req_o.aw = slv_req_i.aw; + // Lock the valid on the master port if it has been given. + logic lock_aw_q, lock_aw_d, load_aw_lock; + // W channel needs a FIFO to determine the silencing of the strobe signal. + logic w_full, w_empty, w_push, w_pop; + sel_t aw_sel, w_sel; + + // AW channel handshake control + always_comb begin : proc_aw_handshake + // default assignment + load_aw_lock = 1'b0; // the FF is toggling back and forth when loaded. + mst_req_o.aw_valid = 1'b0; + slv_res_o.aw_ready = 1'b0; + w_push = 1'b0; + + if (lock_aw_q) begin + mst_req_o.aw_valid = 1'b1; + slv_res_o.aw_ready = mst_res_i.aw_ready; + if (mst_res_i.aw_ready) begin + load_aw_lock = 1'b1; + end + end else begin + // Only connect handshake if there is space in the FIFO + if (!w_full) begin + mst_req_o.aw_valid = slv_req_i.aw_valid; + slv_res_o.aw_ready = mst_res_i.aw_ready; + // If there is a valid on the slave port, push the FIFO + if (slv_req_i.aw_valid) begin + w_push = 1'b1; + // When no transaction, lock AW + if (!mst_res_i.aw_ready) begin + load_aw_lock = 1'b1; + end + end + end + end + end + assign lock_aw_d = ~lock_aw_q; + `FFLARN(lock_aw_q, lock_aw_d, load_aw_lock, 1'b0, clk_i, rst_ni) + + // The selection comes from part of the AW address. + assign aw_sel = sel_t'(slv_req_i.aw.addr >> SelOffset); + + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( UpsizeFactor ), + .dtype ( sel_t ) + ) i_fifo_w_sel ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( w_full ), + .empty_o ( w_empty ), + .usage_o ( /*not used*/ ), + .data_i ( aw_sel ), + .push_i ( w_push ), + .data_o ( w_sel ), + .pop_i ( w_pop ) + ); + // Pop if there is a W transaction on the master port. + assign w_pop = mst_req_o.w_valid & mst_res_i.w_ready; + + // Replicate Data but silence strobe signal. + assign mst_req_o.w = axi_lite_mst_w_t'{ + data: {UpsizeFactor{slv_req_i.w.data}}, + strb: {AxiMstPortStrbWidth{1'b0}} | (slv_req_i.w.strb << (w_sel * AxiSlvPortStrbWidth)), + default: '0 + }; + + // Connect W handshake if the selection is in the FIFO + assign mst_req_o.w_valid = slv_req_i.w_valid & ~w_empty; + assign slv_res_o.w_ready = mst_res_i.w_ready & ~w_empty; + + + // B channel can be passed through + assign slv_res_o.b = mst_res_i.b; + assign slv_res_o.b_valid = mst_res_i.b_valid; + assign mst_req_o.b_ready = slv_req_i.b_ready; + + + // AR channel can be passed through, however block handshake if FIFO is full. + assign mst_req_o.ar = slv_req_i.ar; + // Lock the valid on the master port if it has been given. + logic lock_ar_q, lock_ar_d, load_ar_lock; + // W channel needs a FIFO to determine the silencing of the strobe signal. + logic r_full, r_empty, r_push, r_pop; + sel_t ar_sel, r_sel; + + // AW channel handshake control + always_comb begin : proc_ar_handshake + // default assignment + load_ar_lock = 1'b0; // the FF is toggling back and forth when loaded. + mst_req_o.ar_valid = 1'b0; + slv_res_o.ar_ready = 1'b0; + r_push = 1'b0; + + if (lock_ar_q) begin + mst_req_o.ar_valid = 1'b1; + slv_res_o.ar_ready = mst_res_i.ar_ready; + if (mst_res_i.ar_ready) begin + load_ar_lock = 1'b1; + end + end else begin + // Only connect handshake if there is space in the FIFO + if (!r_full) begin + mst_req_o.ar_valid = slv_req_i.ar_valid; + slv_res_o.ar_ready = mst_res_i.ar_ready; + // If there is a valid on the slave port, push the FIFO + if (slv_req_i.ar_valid) begin + r_push = 1'b1; + // When no transaction, lock AW + if (!mst_res_i.ar_ready) begin + load_ar_lock = 1'b1; + end + end + end + end + end + assign lock_ar_d = ~lock_ar_q; + `FFLARN(lock_ar_q, lock_ar_d, load_ar_lock, 1'b0, clk_i, rst_ni) + + // The selection comes from part of the AW address. + assign ar_sel = sel_t'(slv_req_i.ar.addr >> SelOffset); + + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( UpsizeFactor ), + .dtype ( sel_t ) + ) i_fifo_r_sel ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( r_full ), + .empty_o ( r_empty ), + .usage_o ( /*not used*/ ), + .data_i ( ar_sel ), + .push_i ( r_push ), + .data_o ( r_sel ), + .pop_i ( r_pop ) + ); + // Pop if there is a R transaction on the slave port. + assign r_pop = slv_res_o.r_valid & slv_req_i.r_ready; + + // R channel has to be cut out + assign slv_res_o.r = axi_lite_slv_r_t'{ + data: mst_res_i.r.data[(r_sel*AxiSlvPortDataWidth)+:AxiSlvPortDataWidth], + resp: mst_res_i.r.resp, + default: '0 + }; + // Connect R handshake if there is something in the FIFO. + assign slv_res_o.r_valid = mst_res_i.r_valid & ~r_empty; + assign mst_req_o.r_ready = slv_req_i.r_ready & ~r_empty; + + end else begin : gen_passthrough + assign mst_req_o = slv_req_i; + assign slv_res_o = mst_res_i; + end + + // Assertions, check params + // pragma translate_off + `ifndef VERILATOR + initial begin + assume (AxiAddrWidth > 0) else $fatal(1, "AXI address width has to be > 0"); + assume (AxiSlvPortDataWidth > 8) else $fatal(1, "AxiSlvPortDataWidth has to be > 8"); + assume (AxiMstPortDataWidth > 8) else $fatal(1, "AxiMstPortDataWidth has to be > 8"); + assume ($onehot(AxiSlvPortDataWidth)) else $fatal(1, "AxiSlvPortDataWidth must be power of 2"); + assume ($onehot(AxiMstPortDataWidth)) else $fatal(1, "AxiMstPortDataWidth must be power of 2"); + end + default disable iff (~rst_ni); + stable_aw: assert property (@(posedge clk_i) + (mst_req_o.aw_valid && !mst_res_i.aw_ready) |=> $stable(mst_req_o.aw)) else + $fatal(1, "AW must remain stable until handshake happened."); + stable_w: assert property (@(posedge clk_i) + (mst_req_o.w_valid && !mst_res_i.w_ready) |=> $stable(mst_req_o.w)) else + $fatal(1, "W must remain stable until handshake happened."); + stable_b: assert property (@(posedge clk_i) + (slv_res_o.b_valid && !slv_req_i.b_ready) |=> $stable(slv_res_o.b)) else + $fatal(1, "B must remain stable until handshake happened."); + stable_ar: assert property (@(posedge clk_i) + (mst_req_o.ar_valid && !mst_res_i.ar_ready) |=> $stable(mst_req_o.ar)) else + $fatal(1, "AR must remain stable until handshake happened."); + stable_r: assert property (@(posedge clk_i) + (slv_res_o.r_valid && !slv_req_i.r_ready) |=> $stable(slv_res_o.r)) else + $fatal(1, "R must remain stable until handshake happened."); + `endif + // pragma translate_on +endmodule + +/// Interface wrapper for `axi_lite_dw_converter`. +`include "axi/typedef.svh" +`include "axi/assign.svh" +module axi_lite_dw_converter_intf #( + /// AXI4-Lite address width of the ports. + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4-Lite data width of the slave port. + parameter int unsigned AXI_SLV_PORT_DATA_WIDTH = 32'd0, + /// AXI4-Lite data width of the master port. + parameter int unsigned AXI_MST_PORT_DATA_WIDTH = 32'd0 +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchrounous reset, active low. + input logic rst_ni, + /// Slave port interface. + AXI_LITE.Slave slv, + /// Master port interface. + AXI_LITE.Master mst +); + // AXI configuration + localparam int unsigned AxiStrbWidthSlv = AXI_SLV_PORT_DATA_WIDTH / 32'd8; + localparam int unsigned AxiStrbWidthMst = AXI_MST_PORT_DATA_WIDTH / 32'd8; + // Type definitions + typedef logic [AXI_ADDR_WIDTH-1:0] lite_addr_t; + typedef logic [AXI_SLV_PORT_DATA_WIDTH-1:0] lite_data_slv_t; + typedef logic [AxiStrbWidthSlv-1:0] lite_strb_slv_t; + typedef logic [AXI_MST_PORT_DATA_WIDTH-1:0] lite_data_mst_t; + typedef logic [AxiStrbWidthMst-1:0] lite_strb_mst_t; + + + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, lite_addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_slv_t, lite_data_slv_t, lite_strb_slv_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_mst_t, lite_data_mst_t, lite_strb_mst_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, lite_addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_slv_t, lite_data_slv_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_mst_t, lite_data_mst_t) + + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_slv_t, aw_chan_lite_t, w_chan_lite_slv_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_slv_t, b_chan_lite_t, r_chan_lite_slv_t) + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_mst_t, aw_chan_lite_t, w_chan_lite_mst_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_mst_t, b_chan_lite_t, r_chan_lite_mst_t) + + req_lite_slv_t slv_req; + res_lite_slv_t slv_res; + req_lite_mst_t mst_req; + res_lite_mst_t mst_res; + + `AXI_LITE_ASSIGN_TO_REQ(slv_req, slv) + `AXI_LITE_ASSIGN_FROM_RESP(slv, slv_res) + `AXI_LITE_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_LITE_ASSIGN_TO_RESP(mst_res, mst) + + axi_lite_dw_converter #( + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiSlvPortDataWidth ( AXI_SLV_PORT_DATA_WIDTH ), + .AxiMstPortDataWidth ( AXI_MST_PORT_DATA_WIDTH ), + .axi_lite_aw_t ( aw_chan_lite_t ), + .axi_lite_slv_w_t ( w_chan_lite_slv_t ), + .axi_lite_mst_w_t ( w_chan_lite_mst_t ), + .axi_lite_b_t ( b_chan_lite_t ), + .axi_lite_ar_t ( ar_chan_lite_t ), + .axi_lite_slv_r_t ( r_chan_lite_slv_t ), + .axi_lite_mst_r_t ( r_chan_lite_mst_t ), + .axi_lite_slv_req_t ( req_lite_slv_t ), + .axi_lite_slv_res_t ( res_lite_slv_t ), + .axi_lite_mst_req_t ( req_lite_mst_t ), + .axi_lite_mst_res_t ( res_lite_mst_t ) + ) i_axi_lite_dw_converter ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_res_o ( slv_res ), + .mst_req_o ( mst_req ), + .mst_res_i ( mst_res ) + ); +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_from_mem.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_from_mem.sv new file mode 100644 index 000000000..e13793b37 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_from_mem.sv @@ -0,0 +1,247 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Wolfgang Roenninger +// - Nicole Narr + +/// Protocol adapter which translates memory requests to the AXI4-Lite protocol. +/// +/// This module acts like an SRAM and makes AXI4-Lite requests downstream. +/// +/// Supports multiple outstanding requests and will have responses for reads **and** writes. +/// Response latency is not fixed and for sure **not 1** and depends on the AXI4-Lite memory system. +/// The `mem_rsp_valid_o` can have multiple cycles of latency from the corresponding `mem_gnt_o`. +/// (Was called `mem_to_axi_lite` - originating from https://github.com/pulp-platform/snitch) +module axi_lite_from_mem #( + /// Memory request address width. + parameter int unsigned MemAddrWidth = 32'd0, + /// AXI4-Lite address width. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width in bit of the memory request data **and** the Axi4-Lite data channels. + parameter int unsigned DataWidth = 32'd0, + /// How many requests can be in flight at the same time. (Depth of the response mux FIFO). + parameter int unsigned MaxRequests = 32'd0, + /// Protection signal the module should emit on the AXI4-Lite transactions. + parameter axi_pkg::prot_t AxiProt = 3'b000, + /// AXI4-Lite request struct definition. + parameter type axi_req_t = logic, + /// AXI4-Lite response struct definition. + parameter type axi_rsp_t = logic, + /// Dependent parameter do **not** overwrite! + /// + /// Memory address type, derived from `MemAddrWidth`. + parameter type mem_addr_t = logic[MemAddrWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// AXI4-Lite address type, derived from `AxiAddrWidth`. + parameter type axi_addr_t = logic[AxiAddrWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// Data type for read and write data, derived from `DataWidth`. + /// This is the same for the memory request side **and** the AXI4-Lite `W` and `R` channels. + parameter type data_t = logic[DataWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// Byte enable / AXI4-Lite strobe type, derived from `DataWidth`. + parameter type strb_t = logic[DataWidth/8-1:0] +) ( + /// Clock input, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Memory slave port, request is active. + input logic mem_req_i, + /// Memory slave port, request address. + /// + /// Byte address, will be extended or truncated to match `AxiAddrWidth`. + input mem_addr_t mem_addr_i, + /// Memory slave port, request is a write. + /// + /// `0`: Read request. + /// `1`: Write request. + input logic mem_we_i, + /// Memory salve port, write data for request. + input data_t mem_wdata_i, + /// Memory slave port, write byte enable for request. + /// + /// Active high. + input strb_t mem_be_i, + /// Memory request is granted. + output logic mem_gnt_o, + /// Memory slave port, response is valid. For each request, regardless if read or write, + /// this will be active once for one cycle. + output logic mem_rsp_valid_o, + /// Memory slave port, response read data. This is forwarded directly from the AXI4-Lite + /// `R` channel. Only valid for responses generated by a read request. + output data_t mem_rsp_rdata_o, + /// Memory request encountered an error. This is forwarded from the AXI4-Lite error response. + output logic mem_rsp_error_o, + /// AXI4-Lite master port, request output. + output axi_req_t axi_req_o, + /// AXI4-Lite master port, response input. + input axi_rsp_t axi_rsp_i +); + `include "common_cells/registers.svh" + + // Response FIFO control signals. + logic fifo_full, fifo_empty; + // Bookkeeping for sent write beats. + logic aw_sent_q, aw_sent_d; + logic w_sent_q, w_sent_d; + + // Control for translating request to the AXI4-Lite `AW`, `W` and `AR` channels. + always_comb begin + // Default assignments. + axi_req_o.aw = '0; + axi_req_o.aw.addr = axi_addr_t'(mem_addr_i); + axi_req_o.aw.prot = AxiProt; + axi_req_o.aw_valid = 1'b0; + axi_req_o.w = '0; + axi_req_o.w.data = mem_wdata_i; + axi_req_o.w.strb = mem_be_i; + axi_req_o.w_valid = 1'b0; + axi_req_o.ar = '0; + axi_req_o.ar.addr = axi_addr_t'(mem_addr_i); + axi_req_o.ar.prot = AxiProt; + axi_req_o.ar_valid = 1'b0; + // This is also the push signal for the response FIFO. + mem_gnt_o = 1'b0; + // Bookkeeping about sent write channels. + aw_sent_d = aw_sent_q; + w_sent_d = w_sent_q; + + // Control for Request to AXI4-Lite translation. + if (mem_req_i && !fifo_full) begin + if (!mem_we_i) begin + // It is a read request. + axi_req_o.ar_valid = 1'b1; + mem_gnt_o = axi_rsp_i.ar_ready; + end else begin + // Is is a write request, decouple `AW` and `W` channels. + unique case ({aw_sent_q, w_sent_q}) + 2'b00 : begin + // None of the AXI4-Lite writes have been sent jet. + axi_req_o.aw_valid = 1'b1; + axi_req_o.w_valid = 1'b1; + unique case ({axi_rsp_i.aw_ready, axi_rsp_i.w_ready}) + 2'b01 : begin // W is sent, still needs AW. + w_sent_d = 1'b1; + end + 2'b10 : begin // AW is sent, still needs W. + aw_sent_d = 1'b1; + end + 2'b11 : begin // Both are transmitted, grant the write request. + mem_gnt_o = 1'b1; + end + default : /* do nothing */; + endcase + end + 2'b10 : begin + // W has to be sent. + axi_req_o.w_valid = 1'b1; + if (axi_rsp_i.w_ready) begin + aw_sent_d = 1'b0; + mem_gnt_o = 1'b1; + end + end + 2'b01 : begin + // AW has to be sent. + axi_req_o.aw_valid = 1'b1; + if (axi_rsp_i.aw_ready) begin + w_sent_d = 1'b0; + mem_gnt_o = 1'b1; + end + end + default : begin + // Failsafe go to IDLE. + aw_sent_d = 1'b0; + w_sent_d = 1'b0; + end + endcase + end + end + end + + `FFARN(aw_sent_q, aw_sent_d, 1'b0, clk_i, rst_ni) + `FFARN(w_sent_q, w_sent_d, 1'b0, clk_i, rst_ni) + + // Select which response should be forwarded. `1` write response, `0` read response. + logic rsp_sel; + + fifo_v3 #( + .FALL_THROUGH ( 1'b0 ), // No fallthrough for one cycle delay before ready on AXI. + .DEPTH ( MaxRequests ), + .dtype ( logic ) + ) i_fifo_rsp_mux ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( fifo_full ), + .empty_o ( fifo_empty ), + .usage_o ( /*not used*/ ), + .data_i ( mem_we_i ), + .push_i ( mem_gnt_o ), + .data_o ( rsp_sel ), + .pop_i ( mem_rsp_valid_o ) + ); + + // Response selection control. + // If something is in the FIFO, the corresponding channel is ready. + assign axi_req_o.b_ready = !fifo_empty && rsp_sel; + assign axi_req_o.r_ready = !fifo_empty && !rsp_sel; + // Read data is directly forwarded. + assign mem_rsp_rdata_o = axi_rsp_i.r.data; + // Error is taken from the respective channel. + assign mem_rsp_error_o = rsp_sel ? + (axi_rsp_i.b.resp inside {axi_pkg::RESP_SLVERR, axi_pkg::RESP_DECERR}) : + (axi_rsp_i.r.resp inside {axi_pkg::RESP_SLVERR, axi_pkg::RESP_DECERR}); + // Mem response is valid if the handshaking on the respective channel occurs. + // Can not happen at the same time as ready is set from the FIFO. + // This serves as the pop signal for the FIFO. + assign mem_rsp_valid_o = (axi_rsp_i.b_valid && axi_req_o.b_ready) || + (axi_rsp_i.r_valid && axi_req_o.r_ready); + + // pragma translate_off + `ifndef SYNTHESIS + `ifndef VERILATOR + initial begin : proc_assert + assert (MemAddrWidth > 32'd0) else $fatal(1, "MemAddrWidth has to be greater than 0!"); + assert (AxiAddrWidth > 32'd0) else $fatal(1, "AxiAddrWidth has to be greater than 0!"); + assert (DataWidth inside {32'd32, 32'd64}) else + $fatal(1, "DataWidth has to be either 32 or 64 bit!"); + assert (MaxRequests > 32'd0) else $fatal(1, "MaxRequests has to be greater than 0!"); + assert (AxiAddrWidth == $bits(axi_req_o.aw.addr)) else + $fatal(1, "AxiAddrWidth has to match axi_req_o.aw.addr!"); + assert (AxiAddrWidth == $bits(axi_req_o.ar.addr)) else + $fatal(1, "AxiAddrWidth has to match axi_req_o.ar.addr!"); + assert (DataWidth == $bits(axi_req_o.w.data)) else + $fatal(1, "DataWidth has to match axi_req_o.w.data!"); + assert (DataWidth/8 == $bits(axi_req_o.w.strb)) else + $fatal(1, "DataWidth / 8 has to match axi_req_o.w.strb!"); + assert (DataWidth == $bits(axi_rsp_i.r.data)) else + $fatal(1, "DataWidth has to match axi_rsp_i.r.data!"); + end + default disable iff (~rst_ni); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> mem_req_i) else + $fatal(1, "It is not allowed to deassert the request if it was not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_addr_i)) else + $fatal(1, "mem_addr_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_we_i)) else + $fatal(1, "mem_we_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_wdata_i)) else + $fatal(1, "mem_wdata_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_be_i)) else + $fatal(1, "mem_be_i has to be stable if request is not granted!"); + `endif + `endif + // pragma translate_on +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_lfsr.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_lfsr.sv new file mode 100644 index 000000000..bf8e0f5e5 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_lfsr.sv @@ -0,0 +1,223 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +`include "common_cells/registers.svh" + +/// AXI4 Lite LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lite_lfsr #( + /// AXI4 Lite Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Lite request struct definition + parameter type axi_lite_req_t = logic, + /// AXI4 Lite response struct definition + parameter type axi_lite_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 Lite request struct + input axi_lite_req_t req_i, + /// AXI4 Lite response struct + output axi_lite_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + logic w_lfsr_en; + logic r_lfsr_en; + + logic w_b_fifo_ready; + + // LFSR outputs + logic [DataWidth-1:0] w_data_in, w_data_out; + + // AW (ignored) + assign rsp_o.aw_ready = !w_ser_en_i; + + // W + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_w ( + .clk_i, + .rst_ni, + .en_i ( w_lfsr_en ), + .ser_data_i ( w_ser_data_i ), + .ser_data_o ( w_ser_data_o ), + .ser_en_i ( w_ser_en_i ), + .inp_en_i ( w_lfsr_en ), + .data_i ( w_data_in ), + .data_o ( w_data_out ) + ); + assign w_lfsr_en = req_i.w_valid & rsp_o.w_ready; + assign rsp_o.w_ready = !w_ser_en_i & w_b_fifo_ready; + + // only write bytes with strobe signal enabled + always_comb begin : gen_data_strb_connect + for (int unsigned i = 0; i < StrbWidth; i++) begin : gen_strb_en + if (req_i.w.strb[i] == 1'b0) begin + w_data_in[i*8+:8] = w_data_out[i*8+:8]; + end else if (req_i.w.strb[i] == 1'b1) begin + w_data_in[i*8+:8] = req_i.w.data[i*8+:8]; + end else begin + w_data_in[i*8+:8] = 'x; + end + end + end + + // B + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 'd1 ), + .DEPTH ( 'd2 ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( 1'b0 ), + .valid_i ( req_i.w_valid ), + .ready_o ( w_b_fifo_ready ), + .data_o ( /* NOT CONNECTED */ ), + .valid_o ( w_b_fifo_valid ), + .ready_i ( req_i.b_ready ) + ); + assign rsp_o.b.resp = axi_pkg::RESP_OKAY; + assign rsp_o.b_valid = w_b_fifo_valid; + + // AR (ignored) + assign rsp_o.ar_ready = !w_ser_en_i; + + // R + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_r ( + .clk_i, + .rst_ni, + .en_i ( r_lfsr_en ), + .ser_data_i ( r_ser_data_i ), + .ser_data_o ( r_ser_data_o ), + .ser_en_i ( r_ser_en_i ), + .inp_en_i ( 1'b0 ), + .data_i ( /* NOT CONNECTED */ ), + .data_o ( rsp_o.r.data ) + ); + assign rsp_o.r.resp = axi_pkg::RESP_OKAY; + assign r_lfsr_en = req_i.r_ready & rsp_o.r_valid; + assign rsp_o.r_valid = !r_ser_en_i; + +endmodule : axi_lite_lfsr + + +/// XOR LFSR with tabs based on the [lfsr_table](https://datacipy.cz/lfsr_table.pdf). LFSR has +/// a serial interface to set the initial state +module axi_opt_lfsr #( + parameter int unsigned Width = 32'd0 +) ( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + input logic en_i, + input logic ser_data_i, + output logic ser_data_o, + input logic ser_en_i, + input logic inp_en_i, + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + /// Number of bits required to hold the LFSR tab configuration + localparam int unsigned LfsrIdxWidth = cf_math_pkg::idx_width(Width); + /// Maximum number of tabs + localparam int unsigned MaxNumTabs = 4; + + /// Type specifying the tap positions + typedef logic [LfsrIdxWidth:0] xnor_entry_t [MaxNumTabs-1:0]; + xnor_entry_t XnorFeedback; + + // the shift register + logic [Width-1:0] reg_d, reg_q; + + // the feedback signal + logic xnor_feedback; + + always_comb begin : gen_register + + // get the parameters + case (Width) + 'd8 : XnorFeedback = { 'd8, 'd6, 'd5, 'd4 }; + 'd16 : XnorFeedback = { 'd16, 'd14, 'd13, 'd11 }; + 'd32 : XnorFeedback = { 'd32, 'd30, 'd26, 'd25 }; + 'd64 : XnorFeedback = { 'd64, 'd63, 'd61, 'd60 }; + 'd128 : XnorFeedback = { 'd128, 'd127, 'd126, 'd119 }; + 'd256 : XnorFeedback = { 'd256, 'd256, 'd521, 'd246 }; + 'd512 : XnorFeedback = { 'd512, 'd510, 'd507, 'd504 }; + 'd1024 : XnorFeedback = { 'd1024, 'd1015, 'd1002, 'd1001 }; + default : XnorFeedback = { 'x, 'x, 'x, 'x }; + endcase + + // shift register functionality + // compression mode + if (inp_en_i) begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_comp_conection + reg_d[i] = reg_q[i+1] ^ data_i[i]; + end + // generation mode + end else begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_gen_conection + reg_d[i] = reg_q[i+1]; + end + end + // serial access mode + if (ser_en_i) begin + // new head element + reg_d[Width-1] = ser_data_i; + // LFSR mode + end else begin + xnor_feedback = reg_q[XnorFeedback[MaxNumTabs-1]-1]; + for (int unsigned t = 0; t < MaxNumTabs - 1; t++) begin : gen_feedback_path + xnor_feedback = xnor_feedback; + if (XnorFeedback[t] != 0) begin + xnor_feedback = xnor_feedback ^ reg_q[XnorFeedback[t]-1]; + end + end + reg_d[Width-1] = inp_en_i ? xnor_feedback ^ data_i[Width-1] : xnor_feedback; + end + end + + // connect outputs + assign ser_data_o = reg_q[0]; + assign data_o = reg_q; + + // state + `FFL(reg_q, reg_d, en_i | ser_en_i, '1, clk_i, rst_ni) + +endmodule : axi_opt_lfsr diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_mux.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_mux.sv index c115e7888..577280279 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_lite_mux.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_mux.sv @@ -27,8 +27,8 @@ module axi_lite_mux #( parameter type b_chan_t = logic, // B LITE Channel Type parameter type ar_chan_t = logic, // AR LITE Channel Type parameter type r_chan_t = logic, // R LITE Channel Type - parameter type req_t = logic, // AXI4-Lite request type - parameter type resp_t = logic, // AXI4-Lite response type + parameter type axi_req_t = logic, // AXI4-Lite request type + parameter type axi_resp_t = logic, // AXI4-Lite response type parameter int unsigned NoSlvPorts = 32'd0, // Number of slave ports // Maximum number of outstanding transactions per write or read parameter int unsigned MaxTrans = 32'd0, @@ -42,20 +42,84 @@ module axi_lite_mux #( parameter bit SpillAr = 1'b1, parameter bit SpillR = 1'b0 ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic test_i, // Test Mode enable + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, // Test Mode enable // slave ports (AXI4-Lite inputs), connect master modules here - input req_t [NoSlvPorts-1:0] slv_reqs_i, - output resp_t [NoSlvPorts-1:0] slv_resps_o, + input axi_req_t [NoSlvPorts-1:0] slv_reqs_i, + output axi_resp_t [NoSlvPorts-1:0] slv_resps_o, // master port (AXI4-Lite output), connect slave module here - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); // pass through if only one slave port if (NoSlvPorts == 32'h1) begin : gen_no_mux - assign mst_req_o = slv_reqs_i[0]; - assign slv_resps_o[0] = mst_resp_i; + spill_register #( + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].aw_valid ), + .ready_o ( slv_resps_o[0].aw_ready ), + .data_i ( slv_reqs_i[0].aw ), + .valid_o ( mst_req_o.aw_valid ), + .ready_i ( mst_resp_i.aw_ready ), + .data_o ( mst_req_o.aw ) + ); + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].w_valid ), + .ready_o ( slv_resps_o[0].w_ready ), + .data_i ( slv_reqs_i[0].w ), + .valid_o ( mst_req_o.w_valid ), + .ready_i ( mst_resp_i.w_ready ), + .data_o ( mst_req_o.w ) + ); + spill_register #( + .T ( b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.b_valid ), + .ready_o ( mst_req_o.b_ready ), + .data_i ( mst_resp_i.b ), + .valid_o ( slv_resps_o[0].b_valid ), + .ready_i ( slv_reqs_i[0].b_ready ), + .data_o ( slv_resps_o[0].b ) + ); + spill_register #( + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].ar_valid ), + .ready_o ( slv_resps_o[0].ar_ready ), + .data_i ( slv_reqs_i[0].ar ), + .valid_o ( mst_req_o.ar_valid ), + .ready_i ( mst_resp_i.ar_ready ), + .data_o ( mst_req_o.ar ) + ); + spill_register #( + .T ( r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.r_valid ), + .ready_o ( mst_req_o.r_ready ), + .data_i ( mst_resp_i.r ), + .valid_o ( slv_resps_o[0].r_valid ), + .ready_i ( slv_reqs_i[0].r_ready ), + .data_o ( slv_resps_o[0].r ) + ); + // other non degenerate cases end else begin : gen_mux // typedef for the FIFO types @@ -219,7 +283,7 @@ module axi_lite_mux #( // W Channel //-------------------------------------- // multiplexer - assign mst_w_chan = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w : '0; + assign mst_w_chan = slv_reqs_i[w_select].w; assign mst_w_valid = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w_valid : 1'b0; for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_slv_w_ready assign slv_resps_o[i].w_ready = mst_w_ready & ~w_fifo_empty & @@ -406,11 +470,11 @@ module axi_lite_mux_intf #( parameter bit SpillAr = 1'b1, parameter bit SpillR = 1'b0 ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic test_i, // Testmode enable - AXI_BUS.Slave slv [NoSlvPorts-1:0], // slave ports - AXI_BUS.Master mst // master port + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, // Testmode enable + AXI_LITE.Slave slv [NoSlvPorts-1:0], // slave ports + AXI_LITE.Master mst // master port ); typedef logic [AxiAddrWidth-1:0] addr_t; @@ -422,13 +486,13 @@ module axi_lite_mux_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t [NoSlvPorts-1:0] slv_reqs; - resp_t [NoSlvPorts-1:0] slv_resps; - req_t mst_req; - resp_t mst_resp; + axi_req_t [NoSlvPorts-1:0] slv_reqs; + axi_resp_t [NoSlvPorts-1:0] slv_resps; + axi_req_t mst_req; + axi_resp_t mst_resp; for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_assign_slv_ports `AXI_LITE_ASSIGN_TO_REQ(slv_reqs[i], slv[i]) @@ -444,6 +508,8 @@ module axi_lite_mux_intf #( .b_chan_t ( b_chan_t ), // B Channel Type .ar_chan_t ( ar_chan_t ), // AR Channel Type .r_chan_t ( r_chan_t ), // R Channel Type + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoSlvPorts ( NoSlvPorts ), // Number of slave ports .MaxTrans ( MaxTrans ), .FallThrough ( FallThrough ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_to_axi.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_to_axi.sv index bbeebec10..f1f59ed02 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_lite_to_axi.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_to_axi.sv @@ -21,8 +21,8 @@ module axi_lite_to_axi #( parameter type req_lite_t = logic, parameter type resp_lite_t = logic, // FULL AXI structs - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( // Slave AXI LITE port input req_lite_t slv_req_lite_i, @@ -30,8 +30,8 @@ module axi_lite_to_axi #( input axi_pkg::cache_t slv_aw_cache_i, input axi_pkg::cache_t slv_ar_cache_i, // Master AXI port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); localparam int unsigned AxiSize = axi_pkg::size_t'($unsigned($clog2(AxiDataWidth/8))); diff --git a/hw/vendor/pulp_platform_axi/src/axi_lite_xbar.sv b/hw/vendor/pulp_platform_axi/src/axi_lite_xbar.sv index 91a24adb8..3c2bb387c 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_lite_xbar.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_lite_xbar.sv @@ -21,24 +21,24 @@ module axi_lite_xbar #( parameter axi_pkg::xbar_cfg_t Cfg = '0, - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, - parameter type req_t = logic, - parameter type resp_t = logic, - parameter type rule_t = axi_pkg::xbar_rule_64_t, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, + parameter type rule_t = axi_pkg::xbar_rule_64_t, // DEPENDENT PARAMETERS, DO NOT OVERWRITE! parameter int unsigned MstIdxWidth = (Cfg.NoMstPorts > 32'd1) ? $clog2(Cfg.NoMstPorts) : 32'd1 ) ( input logic clk_i, input logic rst_ni, input logic test_i, - input req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, - output resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, - output req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, - input resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + input axi_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + output axi_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + output axi_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + input axi_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, input logic [Cfg.NoSlvPorts-1:0][MstIdxWidth-1:0] default_mst_port_i @@ -60,12 +60,12 @@ module axi_lite_xbar #( `AXI_TYPEDEF_RESP_T(full_resp_t, full_b_chan_t, full_r_chan_t) // signals from the axi_lite_demuxes, one index more for decode error routing - req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; - resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; + axi_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; + axi_resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; // signals into the axi_lite_muxes, are of type slave as the multiplexer extends the ID - req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; - resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; + axi_req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; + axi_resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux logic [MstIdxWidth-1:0] dec_aw, dec_ar; @@ -143,8 +143,8 @@ module axi_lite_xbar #( .b_chan_t ( b_chan_t ), // B Channel Type .ar_chan_t ( ar_chan_t ), // AR Channel Type .r_chan_t ( r_chan_t ), // R Channel Type - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoMstPorts ( Cfg.NoMstPorts + 1 ), .MaxTrans ( Cfg.MaxMstTrans ), .FallThrough ( Cfg.FallThrough ), @@ -169,10 +169,10 @@ module axi_lite_xbar #( // typedef as the decode error slave uses full axi axi_lite_to_axi #( .AxiDataWidth ( Cfg.AxiDataWidth ), - .req_lite_t ( req_t ), - .resp_lite_t ( resp_t ), - .req_t ( full_req_t ), - .resp_t ( full_resp_t ) + .req_lite_t ( axi_req_t ), + .resp_lite_t ( axi_resp_t ), + .axi_req_t ( full_req_t ), + .axi_resp_t ( full_resp_t ) ) i_dec_err_conv ( .slv_req_lite_i ( slv_reqs[i][Cfg.NoMstPorts] ), .slv_resp_lite_o ( slv_resps[i][Cfg.NoMstPorts] ), @@ -184,8 +184,8 @@ module axi_lite_xbar #( axi_err_slv #( .AxiIdWidth ( 32'd1 ), // ID width is one as defined as logic above - .req_t ( full_req_t ), // AXI request struct - .resp_t ( full_resp_t ), // AXI response struct + .axi_req_t ( full_req_t ), // AXI request struct + .axi_resp_t ( full_resp_t ), // AXI response struct .Resp ( axi_pkg::RESP_DECERR ), .ATOPs ( 1'b0 ), // no ATOPs in AXI4-Lite .MaxTrans ( 1 ) // Transactions terminate at this slave, and AXI4-Lite @@ -215,8 +215,8 @@ module axi_lite_xbar #( .b_chan_t ( b_chan_t ), // B Channel Type .ar_chan_t ( ar_chan_t ), // AR Channel Type .r_chan_t ( r_chan_t ), // R Channel Type - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoSlvPorts ( Cfg.NoSlvPorts ), // Number of Masters for the module .MaxTrans ( Cfg.MaxSlvTrans ), .FallThrough ( Cfg.FallThrough ), @@ -236,3 +236,70 @@ module axi_lite_xbar #( ); end endmodule + +`include "axi/assign.svh" + +module axi_lite_xbar_intf #( + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_LITE.Slave slv_ports [Cfg.NoSlvPorts-1:0], + AXI_LITE.Master mst_ports [Cfg.NoMstPorts-1:0], + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, + input logic [Cfg.NoSlvPorts-1:0][$clog2(Cfg.NoMstPorts)-1:0] default_mst_port_i +); + + typedef logic [Cfg.AxiAddrWidth -1:0] addr_t; + typedef logic [Cfg.AxiDataWidth -1:0] data_t; + typedef logic [Cfg.AxiDataWidth/8 -1:0] strb_t; + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t [Cfg.NoMstPorts-1:0] mst_reqs; + axi_resp_t [Cfg.NoMstPorts-1:0] mst_resps; + axi_req_t [Cfg.NoSlvPorts-1:0] slv_reqs; + axi_resp_t [Cfg.NoSlvPorts-1:0] slv_resps; + + for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_assign_mst + `AXI_LITE_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) + `AXI_LITE_ASSIGN_TO_RESP(mst_resps[i], mst_ports[i]) + end + + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_assign_slv + `AXI_LITE_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i]) + `AXI_LITE_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i]) + end + + axi_lite_xbar #( + .Cfg (Cfg), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .rule_t ( rule_t ) + ) i_xbar ( + .clk_i, + .rst_ni, + .test_i, + .slv_ports_req_i (slv_reqs ), + .slv_ports_resp_o (slv_resps), + .mst_ports_req_o (mst_reqs ), + .mst_ports_resp_i (mst_resps), + .addr_map_i, + .en_default_mst_port_i, + .default_mst_port_i + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_multicut.sv b/hw/vendor/pulp_platform_axi/src/axi_multicut.sv index 8e5dc2f95..64a354a4c 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_multicut.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_multicut.sv @@ -11,8 +11,8 @@ // // Authors: // - Wolfgang Roenninger -// - Fabian Schuiki // - Andreas Kurth +// - Fabian Schuiki // - Stefan Mach // Multiple AXI4 cuts. @@ -21,23 +21,23 @@ module axi_multicut #( parameter int unsigned NoCuts = 32'd1, // Number of cuts. // AXI channel structs - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, // AXI request & response structs - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low // slave port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // master port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); if (NoCuts == '0) begin : gen_no_cut @@ -46,8 +46,8 @@ module axi_multicut #( assign slv_resp_o = mst_resp_i; end else begin : gen_axi_cut // instantiate all needed cuts - req_t [NoCuts:0] cut_req; - resp_t [NoCuts:0] cut_resp; + axi_req_t [NoCuts:0] cut_req; + axi_resp_t [NoCuts:0] cut_resp; // connect slave to the lowest index assign cut_req[0] = slv_req_i; @@ -56,14 +56,14 @@ module axi_multicut #( // AXI cuts for (genvar i = 0; i < NoCuts; i++) begin : gen_axi_cuts axi_cut #( - .Bypass ( 1'b0 ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .Bypass ( 1'b0 ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_cut ( .clk_i, .rst_ni, @@ -117,11 +117,11 @@ module axi_multicut_intf #( `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, in) `AXI_ASSIGN_FROM_RESP(in, slv_resp) @@ -130,14 +130,14 @@ module axi_multicut_intf #( `AXI_ASSIGN_TO_RESP(mst_resp, out) axi_multicut #( - .NoCuts ( NUM_CUTS ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .NoCuts ( NUM_CUTS ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_multicut ( .clk_i, .rst_ni, @@ -191,11 +191,11 @@ module axi_lite_multicut_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_LITE_ASSIGN_TO_REQ(slv_req, in) `AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp) @@ -204,14 +204,14 @@ module axi_lite_multicut_intf #( `AXI_LITE_ASSIGN_TO_RESP(mst_resp, out) axi_multicut #( - .NoCuts ( NUM_CUTS ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .NoCuts ( NUM_CUTS ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_multicut ( .clk_i, .rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_mux.sv b/hw/vendor/pulp_platform_axi/src/axi_mux.sv index 59ee3ec46..da17e2b8c 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_mux.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_mux.sv @@ -22,6 +22,7 @@ // a response with ID `6'b100110` will be forwarded to slave port 2 (`2'b10`). // register macros +`include "common_cells/assertions.svh" `include "common_cells/registers.svh" module axi_mux #( @@ -69,8 +70,83 @@ module axi_mux #( // pass through if only one slave port if (NoSlvPorts == 32'h1) begin : gen_no_mux - assign mst_req_o = slv_reqs_i[0]; - assign slv_resps_o[0] = mst_resp_i; + spill_register #( + .T ( mst_aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].aw_valid ), + .ready_o ( slv_resps_o[0].aw_ready ), + .data_i ( slv_reqs_i[0].aw ), + .valid_o ( mst_req_o.aw_valid ), + .ready_i ( mst_resp_i.aw_ready ), + .data_o ( mst_req_o.aw ) + ); + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].w_valid ), + .ready_o ( slv_resps_o[0].w_ready ), + .data_i ( slv_reqs_i[0].w ), + .valid_o ( mst_req_o.w_valid ), + .ready_i ( mst_resp_i.w_ready ), + .data_o ( mst_req_o.w ) + ); + spill_register #( + .T ( mst_b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.b_valid ), + .ready_o ( mst_req_o.b_ready ), + .data_i ( mst_resp_i.b ), + .valid_o ( slv_resps_o[0].b_valid ), + .ready_i ( slv_reqs_i[0].b_ready ), + .data_o ( slv_resps_o[0].b ) + ); + spill_register #( + .T ( mst_ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].ar_valid ), + .ready_o ( slv_resps_o[0].ar_ready ), + .data_i ( slv_reqs_i[0].ar ), + .valid_o ( mst_req_o.ar_valid ), + .ready_i ( mst_resp_i.ar_ready ), + .data_o ( mst_req_o.ar ) + ); + spill_register #( + .T ( mst_r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.r_valid ), + .ready_o ( mst_req_o.r_ready ), + .data_i ( mst_resp_i.r ), + .valid_o ( slv_resps_o[0].r_valid ), + .ready_i ( slv_reqs_i[0].r_ready ), + .data_o ( slv_resps_o[0].r ) + ); +// Validate parameters. +// pragma translate_off + `ASSERT_INIT(CorrectIdWidthSlvAw, $bits(slv_reqs_i[0].aw.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthSlvB, $bits(slv_resps_o[0].b.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthSlvAr, $bits(slv_reqs_i[0].ar.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthSlvR, $bits(slv_resps_o[0].r.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstAw, $bits(mst_req_o.aw.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstB, $bits(mst_resp_i.b.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstAr, $bits(mst_req_o.ar.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstR, $bits(mst_resp_i.r.id) == SlvAxiIDWidth) +// pragma translate_on + // other non degenerate cases end else begin : gen_mux diff --git a/hw/vendor/pulp_platform_axi/src/axi_pkg.sv b/hw/vendor/pulp_platform_axi/src/axi_pkg.sv index 92ede558c..4102be0e8 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_pkg.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_pkg.sv @@ -12,33 +12,58 @@ // Authors: // - Andreas Kurth // - Florian Zaruba +// - Thomas Benz // - Wolfgang Roenninger // - Fabian Schuiki +// - Cyril Koenig // - Matheus Cavalcante //! AXI Package /// Contains all necessary type definitions, constants, and generally useful functions. package axi_pkg; - /// AXI Transaction Burst Type. - typedef logic [1:0] burst_t; + /// AXI Transaction Burst Width. + parameter int unsigned BurstWidth = 32'd2; + /// AXI Transaction Response Width. + parameter int unsigned RespWidth = 32'd2; + /// AXI Transaction Cacheability Width. + parameter int unsigned CacheWidth = 32'd4; + /// AXI Transaction Protection Width. + parameter int unsigned ProtWidth = 32'd3; + /// AXI Transaction Quality of Service Width. + parameter int unsigned QosWidth = 32'd4; + /// AXI Transaction Region Width. + parameter int unsigned RegionWidth = 32'd4; + /// AXI Transaction Length Width. + parameter int unsigned LenWidth = 32'd8; + /// AXI Transaction Size Width. + parameter int unsigned SizeWidth = 32'd3; + /// AXI Lock Width. + parameter int unsigned LockWidth = 32'd1; + /// AXI5 Atomic Operation Width. + parameter int unsigned AtopWidth = 32'd6; + /// AXI5 Non-Secure Address Identifier. + parameter int unsigned NsaidWidth = 32'd4; + + /// AXI Transaction Burst Width. + typedef logic [1:0] burst_t; /// AXI Transaction Response Type. - typedef logic [1:0] resp_t; + typedef logic [1:0] resp_t; /// AXI Transaction Cacheability Type. - typedef logic [3:0] cache_t; + typedef logic [3:0] cache_t; /// AXI Transaction Protection Type. - typedef logic [2:0] prot_t; + typedef logic [2:0] prot_t; /// AXI Transaction Quality of Service Type. - typedef logic [3:0] qos_t; + typedef logic [3:0] qos_t; /// AXI Transaction Region Type. typedef logic [3:0] region_t; /// AXI Transaction Length Type. - typedef logic [7:0] len_t; + typedef logic [7:0] len_t; /// AXI Transaction Size Type. - typedef logic [2:0] size_t; + typedef logic [2:0] size_t; /// AXI5 Atomic Operation Type. - typedef logic [5:0] atop_t; // atomic operations + typedef logic [5:0] atop_t; // atomic operations /// AXI5 Non-Secure Address Identifier. - typedef logic [3:0] nsaid_t; + typedef logic [3:0] nsaid_t; /// In a fixed burst: /// - The address is the same for every transfer in the burst. @@ -175,7 +200,7 @@ package axi_pkg; beat_lower_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst, shortint unsigned strobe_width, shortint unsigned i_beat); largest_addr_t _addr = beat_addr(addr, size, len, burst, i_beat); - return _addr - (_addr / strobe_width) * strobe_width; + return shortint'(_addr - (_addr / strobe_width) * strobe_width); endfunction /// Index of highest byte in beat (see A3-51). @@ -290,6 +315,65 @@ package axi_pkg; endcase endfunction + /// AW Width: Returns the width of the AW channel payload + function automatic int unsigned aw_width(int unsigned addr_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + addr_width + LenWidth + SizeWidth + BurstWidth + LockWidth + CacheWidth + + ProtWidth + QosWidth + RegionWidth + AtopWidth + user_width ); + endfunction + + /// W Width: Returns the width of the W channel payload + function automatic int unsigned w_width(int unsigned data_width, int unsigned user_width ); + // Sum the individual bit widths of the signals + return (data_width + data_width / 32'd8 + 32'd1 + user_width); + // ^- StrobeWidth ^- LastWidth + endfunction + + /// B Width: Returns the width of the B channel payload + function automatic int unsigned b_width(int unsigned id_width, int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + RespWidth + user_width); + endfunction + + /// AR Width: Returns the width of the AR channel payload + function automatic int unsigned ar_width(int unsigned addr_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + addr_width + LenWidth + SizeWidth + BurstWidth + LockWidth + CacheWidth + + ProtWidth + QosWidth + RegionWidth + user_width ); + endfunction + + /// R Width: Returns the width of the R channel payload + function automatic int unsigned r_width(int unsigned data_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + data_width + RespWidth + 32'd1 + user_width); + // ^- LastWidth + endfunction + + /// Request Width: Returns the width of the request channel + function automatic int unsigned req_width(int unsigned addr_width, int unsigned data_width, + int unsigned id_width, int unsigned aw_user_width, + int unsigned ar_user_width, int unsigned w_user_width ); + // Sum the individual bit widths of the signals and their handshakes + // v- valids + return (aw_width(addr_width, id_width, aw_user_width) + 32'd1 + + w_width(data_width, w_user_width) + 32'd1 + + ar_width(addr_width, id_width, ar_user_width) + 32'd1 + 32'd1 + 32'd1 ); + // ^- R, ^- B ready + endfunction + + /// Response Width: Returns the width of the response channel + function automatic int unsigned rsp_width(int unsigned data_width, int unsigned id_width, + int unsigned r_user_width, int unsigned b_user_width ); + // Sum the individual bit widths of the signals and their handshakes + // v- valids + return (r_width(data_width, id_width, r_user_width) + 32'd1 + + b_width(id_width, b_user_width) + 32'd1 + 32'd1 + 32'd1 + 32'd1); + // ^- AW, ^- AR, ^- W ready + endfunction + // ATOP[5:0] /// - Sends a single data value with an address. /// - The target swaps the value at the addressed location with the data value that is supplied in @@ -361,27 +445,27 @@ package axi_pkg; // `xbar_latency_e` and `xbar_cfg_t` are documented in `doc/axi_xbar.md`. /// Slice on Demux AW channel. - localparam logic [9:0] DemuxAw = (1 << 9); + localparam bit [9:0] DemuxAw = (1 << 9); /// Slice on Demux W channel. - localparam logic [9:0] DemuxW = (1 << 8); + localparam bit [9:0] DemuxW = (1 << 8); /// Slice on Demux B channel. - localparam logic [9:0] DemuxB = (1 << 7); + localparam bit [9:0] DemuxB = (1 << 7); /// Slice on Demux AR channel. - localparam logic [9:0] DemuxAr = (1 << 6); + localparam bit [9:0] DemuxAr = (1 << 6); /// Slice on Demux R channel. - localparam logic [9:0] DemuxR = (1 << 5); + localparam bit [9:0] DemuxR = (1 << 5); /// Slice on Mux AW channel. - localparam logic [9:0] MuxAw = (1 << 4); + localparam bit [9:0] MuxAw = (1 << 4); /// Slice on Mux W channel. - localparam logic [9:0] MuxW = (1 << 3); + localparam bit [9:0] MuxW = (1 << 3); /// Slice on Mux B channel. - localparam logic [9:0] MuxB = (1 << 2); + localparam bit [9:0] MuxB = (1 << 2); /// Slice on Mux AR channel. - localparam logic [9:0] MuxAr = (1 << 1); + localparam bit [9:0] MuxAr = (1 << 1); /// Slice on Mux R channel. - localparam logic [9:0] MuxR = (1 << 0); + localparam bit [9:0] MuxR = (1 << 0); /// Latency configuration for `axi_xbar`. - typedef enum logic [9:0] { + typedef enum bit [9:0] { NO_LATENCY = 10'b000_00_000_00, CUT_SLV_AX = DemuxAw | DemuxAr, CUT_MST_AX = MuxAw | MuxAr, @@ -393,17 +477,44 @@ package axi_pkg; /// Configuration for `axi_xbar`. typedef struct packed { + /// Number of slave ports of the crossbar. + /// This many master modules are connected to it. int unsigned NoSlvPorts; + /// Number of master ports of the crossbar. + /// This many slave modules are connected to it. int unsigned NoMstPorts; + /// Maximum number of open transactions each master connected to the crossbar can have in + /// flight at the same time. int unsigned MaxMstTrans; + /// Maximum number of open transactions each slave connected to the crossbar can have in + /// flight at the same time. int unsigned MaxSlvTrans; + /// Determine if the internal FIFOs of the crossbar are instantiated in fallthrough mode. + /// 0: No fallthrough + /// 1: Fallthrough bit FallThrough; - xbar_latency_e LatencyMode; + /// The Latency mode of the xbar. This determines if the channels on the ports have + /// a spill register instantiated. + /// Example configurations are provided with the enum `xbar_latency_e`. + bit [9:0] LatencyMode; + /// This is the number of `axi_multicut` stages instantiated in the line cross of the channels. + /// Having multiple stages can potentially add a large number of FFs! + int unsigned PipelineStages; + /// AXI ID width of the salve ports. The ID width of the master ports is determined + /// Automatically. See `axi_mux` for details. int unsigned AxiIdWidthSlvPorts; + /// The used ID portion to determine if a different salve is used for the same ID. + /// See `axi_demux` for details. int unsigned AxiIdUsedSlvPorts; + /// Are IDs unique? bit UniqueIds; + /// AXI4+ATOP address field width. int unsigned AxiAddrWidth; + /// AXI4+ATOP data field width. int unsigned AxiDataWidth; + /// The number of address rules defined for routing of the transactions. + /// Each master port can have multiple rules, should have however at least one. + /// If a transaction can not be routed the xbar will answer with an `axi_pkg::RESP_DECERR`. int unsigned NoAddrRules; } xbar_cfg_t; @@ -420,4 +531,5 @@ package axi_pkg; logic [31:0] start_addr; logic [31:0] end_addr; } xbar_rule_32_t; + endpackage diff --git a/hw/vendor/pulp_platform_axi/src/axi_rw_join.sv b/hw/vendor/pulp_platform_axi/src/axi_rw_join.sv new file mode 100644 index 000000000..6b7191b38 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_rw_join.sv @@ -0,0 +1,110 @@ +// Copyright (c) 2022 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Tobias Senti + +`include "axi/assign.svh" +`include "common_cells/assertions.svh" + +/// Joins a read and a write slave into one single read / write master +/// +/// Connects the ar and r channel of the read slave to the read / write master +/// and the aw, w and b channel of the write slave to the read / write master +module axi_rw_join #( + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // Read Slave + input axi_req_t slv_read_req_i, + output axi_resp_t slv_read_resp_o, + + // Write Slave + input axi_req_t slv_write_req_i, + output axi_resp_t slv_write_resp_o, + + // Read / Write Master + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i +); + + //-------------------------------------- + // Read channel data + //-------------------------------------- + + // Assign Read Structs + `AXI_ASSIGN_AR_STRUCT ( mst_req_o.ar , slv_read_req_i.ar ) + `AXI_ASSIGN_R_STRUCT ( slv_read_resp_o.r , mst_resp_i.r ) + + // Read B channel data + assign slv_read_resp_o.b = '0; + + + //-------------------------------------- + // Read channel handshakes + //-------------------------------------- + + // Read AR channel handshake + assign mst_req_o.ar_valid = slv_read_req_i.ar_valid; + assign slv_read_resp_o.ar_ready = mst_resp_i.ar_ready; + + // Read R channel handshake + assign slv_read_resp_o.r_valid = mst_resp_i.r_valid; + assign mst_req_o.r_ready = slv_read_req_i.r_ready; + + // Read AW, W and B handshake + assign slv_read_resp_o.aw_ready = 1'b0; + assign slv_read_resp_o.w_ready = 1'b0; + assign slv_read_resp_o.b_valid = 1'b0; + + // check for AW and W never to be valid + `ASSERT_NEVER(slv_read_req_aw_valid, slv_read_req_i.aw_valid, clk_i, !rst_ni) + `ASSERT_NEVER(slv_read_req_w_valid, slv_read_req_i.w_valid, clk_i, !rst_ni) + + //-------------------------------------- + // Write channel data + //-------------------------------------- + + // Assign Write Structs + `AXI_ASSIGN_AW_STRUCT ( mst_req_o.aw , slv_write_req_i.aw ) + `AXI_ASSIGN_W_STRUCT ( mst_req_o.w , slv_write_req_i.w ) + `AXI_ASSIGN_B_STRUCT ( slv_write_resp_o.b , mst_resp_i.b ) + + // Write R channel data + assign slv_write_resp_o.r = '0; + + + //-------------------------------------- + // Write channel handshakes + //-------------------------------------- + + // Write AR and R channel handshake + assign slv_write_resp_o.ar_ready = 1'b0; + assign slv_write_resp_o.r_valid = 1'b0; + + // check for AR to never be valid + `ASSERT_NEVER(slv_write_req_ar_valid, slv_write_req_i.ar_valid, clk_i, !rst_ni) + + // Write AW channel handshake + assign mst_req_o.aw_valid = slv_write_req_i.aw_valid; + assign slv_write_resp_o.aw_ready = mst_resp_i.aw_ready; + + // Write W channel handshake + assign mst_req_o.w_valid = slv_write_req_i.w_valid; + assign slv_write_resp_o.w_ready = mst_resp_i.w_ready; + + // Write B channel handshake + assign slv_write_resp_o.b_valid = mst_resp_i.b_valid; + assign mst_req_o.b_ready = slv_write_req_i.b_ready; + +endmodule : axi_rw_join diff --git a/hw/vendor/pulp_platform_axi/src/axi_rw_split.sv b/hw/vendor/pulp_platform_axi/src/axi_rw_split.sv new file mode 100644 index 000000000..6fa96dfa9 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_rw_split.sv @@ -0,0 +1,111 @@ +// Copyright (c) 2022 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Tobias Senti + +`include "axi/assign.svh" +`include "common_cells/assertions.svh" + +/// Splits a single read / write slave into one read and one write master +/// +/// Connects the ar and r channel of the read / write slave to the read master +/// and the aw, w and b channel of the read / write slave to the write master +module axi_rw_split #( + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // Read / Write Slave + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + + // Read Master + output axi_req_t mst_read_req_o, + input axi_resp_t mst_read_resp_i, + + // Write Master + output axi_req_t mst_write_req_o, + input axi_resp_t mst_write_resp_i +); + + //-------------------------------------- + // Read channel data + //-------------------------------------- + + // Assign Read channel structs + `AXI_ASSIGN_AR_STRUCT ( mst_read_req_o.ar , slv_req_i.ar ) + `AXI_ASSIGN_R_STRUCT ( slv_resp_o.r , mst_read_resp_i.r ) + + // Read AW and W channel data + assign mst_read_req_o.aw = '0; + assign mst_read_req_o.w = '0; + + + //-------------------------------------- + // Read channel handshakes + //-------------------------------------- + + // Read AR channel handshake + assign mst_read_req_o.ar_valid = slv_req_i.ar_valid; + assign slv_resp_o.ar_ready = mst_read_resp_i.ar_ready; + + // Read R channel handshake + assign slv_resp_o.r_valid = mst_read_resp_i.r_valid; + assign mst_read_req_o.r_ready = slv_req_i.r_ready; + + // Read AW, W and B handshake + assign mst_read_req_o.aw_valid = 1'b0; + assign mst_read_req_o.w_valid = 1'b0; + assign mst_read_req_o.b_ready = 1'b0; + + // check for B never to be valid + `ASSERT_NEVER(mst_read_resp_b_valid, mst_read_resp_i.b_valid, clk_i, !rst_ni) + + + //-------------------------------------- + // Write channel data + //-------------------------------------- + + // Assign Write channel structs + `AXI_ASSIGN_AW_STRUCT ( mst_write_req_o.aw , slv_req_i.aw ) + `AXI_ASSIGN_W_STRUCT ( mst_write_req_o.w , slv_req_i.w ) + `AXI_ASSIGN_B_STRUCT ( slv_resp_o.b , mst_write_resp_i.b ) + + // Write AR channel data + assign mst_write_req_o.ar = '0; + + + //-------------------------------------- + // Write channel handshakes + //-------------------------------------- + + // Write AR and R channel handshake + assign mst_write_req_o.ar_valid = 1'b0; + assign mst_write_req_o.r_ready = 1'b0; + + // check for R never to be valid + `ASSERT_NEVER(mst_read_resp_r_valid, mst_read_resp_i.r_valid, clk_i, !rst_ni) + + // Write AW channel handshake + assign mst_write_req_o.aw_valid = slv_req_i.aw_valid; + assign slv_resp_o.aw_ready = mst_write_resp_i.aw_ready; + + // Write W channel handshake + assign mst_write_req_o.w_valid = slv_req_i.w_valid; + assign slv_resp_o.w_ready = mst_write_resp_i.w_ready; + + // Write B channel handshake + assign slv_resp_o.b_valid = mst_write_resp_i.b_valid; + assign mst_write_req_o.b_ready = slv_req_i.b_ready; + +endmodule : axi_rw_split diff --git a/hw/vendor/pulp_platform_axi/src/axi_serializer.sv b/hw/vendor/pulp_platform_axi/src/axi_serializer.sv index 30a531591..0b064f29e 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_serializer.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_serializer.sv @@ -13,7 +13,12 @@ // - Andreas Kurth `include "common_cells/registers.svh" + /// Serialize all AXI transactions to a single ID (zero). +/// +/// This module contains one queue with slave port IDs for the read direction and one for the write +/// direction. These queues are used to reconstruct the ID of responses at the slave port. The +/// depth of each queue is defined by `MaxReadTxns` and `MaxWriteTxns`, respectively. module axi_serializer #( /// Maximum number of in flight read transactions. parameter int unsigned MaxReadTxns = 32'd0, @@ -22,22 +27,22 @@ module axi_serializer #( /// AXI4+ATOP ID width. parameter int unsigned AxiIdWidth = 32'd0, /// AXI4+ATOP request struct definition. - parameter type req_t = logic, + parameter type axi_req_t = logic, /// AXI4+ATOP response struct definition. - parameter type resp_t = logic + parameter type axi_resp_t = logic ) ( /// Clock - input logic clk_i, + input logic clk_i, /// Asynchronous reset, active low - input logic rst_ni, + input logic rst_ni, /// Slave port request - input req_t slv_req_i, + input axi_req_t slv_req_i, /// Slave port response - output resp_t slv_resp_o, + output axi_resp_t slv_resp_o, /// Master port request - output req_t mst_req_o, + output axi_req_t mst_req_o, /// Master port response - input resp_t mst_resp_i + input axi_resp_t mst_resp_i ); typedef logic [AxiIdWidth-1:0] id_t; @@ -249,10 +254,10 @@ module axi_serializer_intf #( `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) `AXI_ASSIGN_FROM_REQ(mst, mst_req) @@ -262,8 +267,8 @@ module axi_serializer_intf #( .MaxReadTxns ( MAX_READ_TXNS ), .MaxWriteTxns ( MAX_WRITE_TXNS ), .AxiIdWidth ( AXI_ID_WIDTH ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_serializer ( .clk_i, .rst_ni, diff --git a/hw/vendor/pulp_platform_axi/src/axi_sim_mem.sv b/hw/vendor/pulp_platform_axi/src/axi_sim_mem.sv index 4a9ad57b6..9bf164229 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_sim_mem.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_sim_mem.sv @@ -3,6 +3,9 @@ // // Authors: // - Andreas Kurth +// - Samuel Riedel +// - Michael Rogenmoser +// - Thomas Benz `include "axi/typedef.svh" @@ -14,6 +17,8 @@ /// axi_sim_mem #( ... ) i_sim_mem ( ... ); /// initial begin /// $readmemh("file_with_memory_addrs_and_data.mem", i_sim_mem.mem); +/// $readmemh("file_with_memory_addrs_and_read_errors.mem", i_sim_mem.rerr); +/// $readmemh("file_with_memory_addrs_and_write_errors.mem", i_sim_mem.werr); /// end /// ``` /// `mem` is addressed (or indexed) byte-wise with `AddrWidth`-wide addresses. @@ -28,12 +33,18 @@ module axi_sim_mem #( parameter int unsigned IdWidth = 32'd0, /// AXI User Width. parameter int unsigned UserWidth = 32'd0, + /// Number of request ports + parameter int unsigned NumPorts = 32'd1, /// AXI4 request struct definition - parameter type req_t = logic, + parameter type axi_req_t = logic, /// AXI4 response struct definition - parameter type rsp_t = logic, + parameter type axi_rsp_t = logic, /// Warn on accesses to uninitialized bytes parameter bit WarnUninitialized = 1'b0, + /// Default value for uninitialized memory (undefined, zeros, ones, random) + parameter UninitializedData = "undefined", + /// Clear error on access + parameter bit ClearErrOnAccess = 1'b0, /// Application delay (measured after rising clock edge) parameter time ApplDelay = 0ps, /// Acquisition delay (measured after rising clock edge) @@ -44,9 +55,41 @@ module axi_sim_mem #( /// Active-low reset input logic rst_ni, /// AXI4 request struct - input req_t axi_req_i, + input axi_req_t [NumPorts-1:0] axi_req_i, /// AXI4 response struct - output rsp_t axi_rsp_o + output axi_rsp_t [NumPorts-1:0] axi_rsp_o, + /// Memory monitor write valid. All `mon_w_*` outputs are only valid if this signal is high. + /// A write to the memory is visible on the `mon_w_*` outputs in the clock cycle after it has + /// happened. + output logic [NumPorts-1:0] mon_w_valid_o, + /// Memory monitor write address + output logic [NumPorts-1:0][AddrWidth-1:0] mon_w_addr_o, + /// Memory monitor write data + output logic [NumPorts-1:0][DataWidth-1:0] mon_w_data_o, + /// Memory monitor write ID + output logic [NumPorts-1:0][IdWidth-1:0] mon_w_id_o, + /// Memory monitor write user + output logic [NumPorts-1:0][UserWidth-1:0] mon_w_user_o, + /// Memory monitor write beat count + output axi_pkg::len_t [NumPorts-1:0] mon_w_beat_count_o, + /// Memory monitor write last + output logic [NumPorts-1:0] mon_w_last_o, + /// Memory monitor read valid. All `mon_r_*` outputs are only valid if this signal is high. + /// A read from the memory is visible on the `mon_w_*` outputs in the clock cycle after it has + /// happened. + output logic [NumPorts-1:0] mon_r_valid_o, + /// Memory monitor read address + output logic [NumPorts-1:0][AddrWidth-1:0] mon_r_addr_o, + /// Memory monitor read data + output logic [NumPorts-1:0][DataWidth-1:0] mon_r_data_o, + /// Memory monitor read ID + output logic [NumPorts-1:0][IdWidth-1:0] mon_r_id_o, + /// Memory monitor read user + output logic [NumPorts-1:0][UserWidth-1:0] mon_r_user_o, + /// Memory monitor read beat count + output axi_pkg::len_t [NumPorts-1:0] mon_r_beat_count_o, + /// Memory monitor read last + output logic [NumPorts-1:0] mon_r_last_o ); localparam int unsigned StrbWidth = DataWidth / 8; @@ -61,126 +104,185 @@ module axi_sim_mem #( `AXI_TYPEDEF_AR_CHAN_T(ar_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_t, data_t, id_t, user_t) - logic [7:0] mem[addr_t]; + typedef struct packed { + logic valid; + logic [AddrWidth-1:0] addr; + logic [DataWidth-1:0] data; + logic [IdWidth-1:0] id; + logic [UserWidth-1:0] user; + axi_pkg::len_t beat_count; + logic last; + } monitor_t; - initial begin - automatic ar_t ar_queue[$]; - automatic aw_t aw_queue[$]; - automatic b_t b_queue[$]; - automatic shortint unsigned r_cnt = 0, w_cnt = 0; - axi_rsp_o = '0; - wait (rst_ni); - fork - // AW - forever begin - @(posedge clk_i); - #(ApplDelay); - axi_rsp_o.aw_ready = 1'b1; - #(AcqDelay - ApplDelay); - if (axi_req_i.aw_valid) begin - automatic aw_t aw = axi_req_i.aw; - aw_queue.push_back(aw); - end - end - // W - forever begin - @(posedge clk_i); - #(ApplDelay); - axi_rsp_o.w_ready = 1'b0; - if (aw_queue.size() != 0) begin - axi_rsp_o.w_ready = 1'b1; + monitor_t [NumPorts-1:0] mon_w, mon_r; + logic [7:0] mem[addr_t]; + axi_pkg::resp_t rerr[addr_t] = '{default: axi_pkg::RESP_OKAY}; + axi_pkg::resp_t werr[addr_t] = '{default: axi_pkg::RESP_OKAY}; + + // error happened in write burst + axi_pkg::resp_t [NumPorts-1:0] error_happened = axi_pkg::RESP_OKAY; + + for (genvar i = 0; i < NumPorts; i++) begin + initial begin + automatic ar_t ar_queue[$]; + automatic aw_t aw_queue[$]; + automatic b_t b_queue[$]; + automatic shortint unsigned r_cnt = 0, w_cnt = 0; + axi_rsp_o[i] = '0; + // Monitor interface + mon_w[i] = '0; + mon_r[i] = '0; + wait (rst_ni); + fork + // AW + forever begin + @(posedge clk_i); + #(ApplDelay); + axi_rsp_o[i].aw_ready = 1'b1; #(AcqDelay - ApplDelay); - if (axi_req_i.w_valid) begin - automatic axi_pkg::burst_t burst = aw_queue[0].burst; - automatic axi_pkg::len_t len = aw_queue[0].len; - automatic axi_pkg::size_t size = aw_queue[0].size; - automatic addr_t addr = axi_pkg::beat_addr(aw_queue[0].addr, size, len, burst, - w_cnt); - for (shortint unsigned - i_byte = axi_pkg::beat_lower_byte(addr, size, len, burst, StrbWidth, w_cnt); - i_byte <= axi_pkg::beat_upper_byte(addr, size, len, burst, StrbWidth, w_cnt); - i_byte++) begin - if (axi_req_i.w.strb[i_byte]) begin - automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte; - mem[byte_addr] = axi_req_i.w.data[i_byte*8+:8]; + if (axi_req_i[i].aw_valid) begin + automatic aw_t aw = axi_req_i[i].aw; + aw_queue.push_back(aw); + end + end + // W + forever begin + @(posedge clk_i); + #(ApplDelay); + axi_rsp_o[i].w_ready = 1'b0; + mon_w[i] = '0; + if (aw_queue.size() != 0) begin + axi_rsp_o[i].w_ready = 1'b1; + #(AcqDelay - ApplDelay); + if (axi_req_i[i].w_valid) begin + automatic axi_pkg::burst_t burst = aw_queue[0].burst; + automatic axi_pkg::len_t len = aw_queue[0].len; + automatic axi_pkg::size_t size = aw_queue[0].size; + automatic addr_t addr = axi_pkg::beat_addr(aw_queue[0].addr, size, len, burst, + w_cnt); + mon_w[i].valid = 1'b1; + mon_w[i].addr = addr; + mon_w[i].data = axi_req_i[i].w.data; + mon_w[i].id = aw_queue[0].id; + mon_w[i].user = aw_queue[0].user; + mon_w[i].beat_count = w_cnt; + for (shortint unsigned + i_byte = axi_pkg::beat_lower_byte(aw_queue[0].addr, size, len, burst, StrbWidth, w_cnt); + i_byte <= axi_pkg::beat_upper_byte(aw_queue[0].addr, size, len, burst, StrbWidth, w_cnt); + i_byte++) begin + if (axi_req_i[i].w.strb[i_byte]) begin + automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte; + mem[byte_addr] = axi_req_i[i].w.data[i_byte*8+:8]; + error_happened[i] = axi_pkg::resp_precedence(werr[byte_addr], error_happened[i]); + if (ClearErrOnAccess) + werr[byte_addr] = axi_pkg::RESP_OKAY; + end + end + if (w_cnt == aw_queue[0].len) begin + automatic b_t b_beat = '0; + assert (axi_req_i[i].w.last) else $error("Expected last beat of W burst!"); + b_beat.id = aw_queue[0].id; + b_beat.resp = error_happened[i]; + b_queue.push_back(b_beat); + w_cnt = 0; + mon_w[i].last = 1'b1; + error_happened[i] = axi_pkg::RESP_OKAY; + void'(aw_queue.pop_front()); + end else begin + assert (!axi_req_i[i].w.last) else $error("Did not expect last beat of W burst!"); + w_cnt++; end end - if (w_cnt == aw_queue[0].len) begin - automatic b_t b_beat = '0; - assert (axi_req_i.w.last) else $error("Expected last beat of W burst!"); - b_beat.id = aw_queue[0].id; - b_beat.resp = axi_pkg::RESP_OKAY; - b_queue.push_back(b_beat); - w_cnt = 0; - void'(aw_queue.pop_front()); - end else begin - assert (!axi_req_i.w.last) else $error("Did not expect last beat of W burst!"); - w_cnt++; + end + end + // B + forever begin + @(posedge clk_i); + #(ApplDelay); + axi_rsp_o[i].b_valid = 1'b0; + if (b_queue.size() != 0) begin + axi_rsp_o[i].b = b_queue[0]; + axi_rsp_o[i].b_valid = 1'b1; + #(AcqDelay - ApplDelay); + if (axi_req_i[i].b_ready) begin + void'(b_queue.pop_front()); end end end - end - // B - forever begin - @(posedge clk_i); - #(ApplDelay); - axi_rsp_o.b_valid = 1'b0; - if (b_queue.size() != 0) begin - axi_rsp_o.b = b_queue[0]; - axi_rsp_o.b_valid = 1'b1; + // AR + forever begin + @(posedge clk_i); + #(ApplDelay); + axi_rsp_o[i].ar_ready = 1'b1; #(AcqDelay - ApplDelay); - if (axi_req_i.b_ready) begin - void'(b_queue.pop_front()); + if (axi_req_i[i].ar_valid) begin + automatic ar_t ar = axi_req_i[i].ar; + ar_queue.push_back(ar); end end - end - // AR - forever begin - @(posedge clk_i); - #(ApplDelay); - axi_rsp_o.ar_ready = 1'b1; - #(AcqDelay - ApplDelay); - if (axi_req_i.ar_valid) begin - automatic ar_t ar = axi_req_i.ar; - ar_queue.push_back(ar); - end - end - // R - forever begin - @(posedge clk_i); - #(ApplDelay); - axi_rsp_o.r_valid = 1'b0; - if (ar_queue.size() != 0) begin - automatic axi_pkg::burst_t burst = ar_queue[0].burst; - automatic axi_pkg::len_t len = ar_queue[0].len; - automatic axi_pkg::size_t size = ar_queue[0].size; - automatic addr_t addr = axi_pkg::beat_addr(ar_queue[0].addr, size, len, burst, r_cnt); - automatic r_t r_beat = '0; - r_beat.data = 'x; - r_beat.id = ar_queue[0].id; - r_beat.resp = axi_pkg::RESP_OKAY; - for (shortint unsigned - i_byte = axi_pkg::beat_lower_byte(addr, size, len, burst, StrbWidth, r_cnt); - i_byte <= axi_pkg::beat_upper_byte(addr, size, len, burst, StrbWidth, r_cnt); - i_byte++) begin - automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte; - if (!mem.exists(byte_addr)) begin - if (WarnUninitialized) begin - $warning("Access to non-initialized byte at address 0x%016x by ID 0x%x.", byte_addr, - r_beat.id); + // R + forever begin + @(posedge clk_i); + #(ApplDelay); + axi_rsp_o[i].r_valid = 1'b0; + mon_r[i] = '0; + if (ar_queue.size() != 0) begin + automatic axi_pkg::burst_t burst = ar_queue[0].burst; + automatic axi_pkg::len_t len = ar_queue[0].len; + automatic axi_pkg::size_t size = ar_queue[0].size; + automatic addr_t addr = axi_pkg::beat_addr(ar_queue[0].addr, size, len, burst, r_cnt); + automatic r_t r_beat = '0; + automatic data_t r_data = 'x; // compatibility reasons + r_beat.data = 'x; + r_beat.id = ar_queue[0].id; + r_beat.resp = axi_pkg::RESP_OKAY; + for (shortint unsigned + i_byte = axi_pkg::beat_lower_byte(ar_queue[0].addr, size, len, burst, StrbWidth, r_cnt); + i_byte <= axi_pkg::beat_upper_byte(ar_queue[0].addr, size, len, burst, StrbWidth, r_cnt); + i_byte++) begin + automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte; + if (!mem.exists(byte_addr)) begin + if (WarnUninitialized) begin + $warning("Access to non-initialized byte at address 0x%016x by ID 0x%x.", byte_addr, + r_beat.id); + end + case (UninitializedData) + "random": + r_data[i_byte*8+:8] = $urandom; + "ones": + r_data[i_byte*8+:8] = '1; + "zeros": + r_data[i_byte*8+:8] = '0; + default: + r_data[i_byte*8+:8] = 'x; + endcase + end else begin + r_data[i_byte*8+:8] = mem[byte_addr]; + end + r_beat.resp = axi_pkg::resp_precedence(rerr[byte_addr], r_beat.resp); + if (ClearErrOnAccess & axi_req_i[i].r_ready) begin + rerr[byte_addr] = axi_pkg::RESP_OKAY; end - r_beat.data[i_byte*8+:8] = 'x; - end else begin - r_beat.data[i_byte*8+:8] = mem[byte_addr]; end - end - if (r_cnt == ar_queue[0].len) begin - r_beat.last = 1'b1; - end - axi_rsp_o.r = r_beat; - axi_rsp_o.r_valid = 1'b1; - #(AcqDelay - ApplDelay); - if (axi_req_i.r_ready) begin + r_beat.data = r_data; + if (r_cnt == ar_queue[0].len) begin + r_beat.last = 1'b1; + mon_r[i].last = 1'b1; + end + axi_rsp_o[i].r = r_beat; + axi_rsp_o[i].r_valid = 1'b1; + mon_r[i].valid = 1'b1; + mon_r[i].addr = addr; + mon_r[i].data = r_beat.data; + mon_r[i].id = r_beat.id; + mon_r[i].user = ar_queue[0].user; + mon_r[i].beat_count = r_cnt; + #(AcqDelay - ApplDelay); + while (!axi_req_i[i].r_ready) begin + @(posedge clk_i); + #(AcqDelay); + mon_r[i] = '0; + end if (r_beat.last) begin r_cnt = 0; void'(ar_queue.pop_front()); @@ -189,8 +291,48 @@ module axi_sim_mem #( end end end + join + end + + // Assign the monitor output in the next clock cycle. Rationale: We only know whether we are + // writing until after `AcqDelay`. This means we could only provide the monitoring output for + // writes after `AcqDelay` in the same cycle, which is incompatible with ATI timing. Thus, we + // provide the monitoring output for writes (and for uniformity also for reads) in the next clock + // cycle. + initial begin + mon_w_valid_o[i] = '0; + mon_w_addr_o[i] = '0; + mon_w_data_o[i] = '0; + mon_w_id_o[i] = '0; + mon_w_user_o[i] = '0; + mon_w_beat_count_o[i] = '0; + mon_w_last_o[i] = '0; + mon_r_valid_o[i] = '0; + mon_r_addr_o[i] = '0; + mon_r_data_o[i] = '0; + mon_r_id_o[i] = '0; + mon_r_user_o[i] = '0; + mon_r_beat_count_o[i] = '0; + mon_r_last_o[i] = '0; + wait (rst_ni); + forever begin + @(posedge clk_i); + mon_w_valid_o[i] <= #(ApplDelay) mon_w[i].valid; + mon_w_addr_o[i] <= #(ApplDelay) mon_w[i].addr; + mon_w_data_o[i] <= #(ApplDelay) mon_w[i].data; + mon_w_id_o[i] <= #(ApplDelay) mon_w[i].id; + mon_w_user_o[i] <= #(ApplDelay) mon_w[i].user; + mon_w_beat_count_o[i] <= #(ApplDelay) mon_w[i].beat_count; + mon_w_last_o[i] <= #(ApplDelay) mon_w[i].last; + mon_r_valid_o[i] <= #(ApplDelay) mon_r[i].valid; + mon_r_addr_o[i] <= #(ApplDelay) mon_r[i].addr; + mon_r_data_o[i] <= #(ApplDelay) mon_r[i].data; + mon_r_id_o[i] <= #(ApplDelay) mon_r[i].id; + mon_r_user_o[i] <= #(ApplDelay) mon_r[i].user; + mon_r_beat_count_o[i] <= #(ApplDelay) mon_r[i].beat_count; + mon_r_last_o[i] <= #(ApplDelay) mon_r[i].last; end - join + end end // Parameter Assertions @@ -202,3 +344,172 @@ module axi_sim_mem #( end endmodule + + +`include "axi/assign.svh" + +/// Interface variant of [`axi_sim_mem`](module.axi_sim_mem). +/// +/// See the documentation of the main module for the definition of ports and parameters. +module axi_sim_mem_intf #( + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0, + parameter bit WARN_UNINITIALIZED = 1'b0, + parameter UNINITIALIZED_DATA = "undefined", + parameter bit ClearErrOnAccess = 1'b0, + parameter time APPL_DELAY = 0ps, + parameter time ACQ_DELAY = 0ps +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave axi_slv, + output logic mon_w_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] mon_w_addr_o, + output logic [AXI_DATA_WIDTH-1:0] mon_w_data_o, + output logic [AXI_ID_WIDTH-1:0] mon_w_id_o, + output logic [AXI_USER_WIDTH-1:0] mon_w_user_o, + output axi_pkg::len_t mon_w_beat_count_o, + output logic mon_w_last_o, + output logic mon_r_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] mon_r_addr_o, + output logic [AXI_DATA_WIDTH-1:0] mon_r_data_o, + output logic [AXI_ID_WIDTH-1:0] mon_r_id_o, + output logic [AXI_USER_WIDTH-1:0] mon_r_user_o, + output axi_pkg::len_t mon_r_beat_count_o, + output logic mon_r_last_o +); + + typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t; + typedef logic [AXI_ID_WIDTH-1:0] axi_id_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t; + typedef logic [AXI_USER_WIDTH-1:0] axi_user_t; + `AXI_TYPEDEF_ALL(axi, axi_addr_t, axi_id_t, axi_data_t, axi_strb_t, axi_user_t) + + axi_req_t axi_req; + axi_resp_t axi_rsp; + + `AXI_ASSIGN_TO_REQ(axi_req, axi_slv) + `AXI_ASSIGN_FROM_RESP(axi_slv, axi_rsp) + + axi_sim_mem #( + .AddrWidth (AXI_ADDR_WIDTH), + .DataWidth (AXI_DATA_WIDTH), + .IdWidth (AXI_ID_WIDTH), + .UserWidth (AXI_USER_WIDTH), + .axi_req_t (axi_req_t), + .axi_rsp_t (axi_resp_t), + .WarnUninitialized (WARN_UNINITIALIZED), + .UninitializedData (UNINITIALIZED_DATA), + .ClearErrOnAccess (ClearErrOnAccess), + .ApplDelay (APPL_DELAY), + .AcqDelay (ACQ_DELAY) + ) i_sim_mem ( + .clk_i, + .rst_ni, + .axi_req_i (axi_req), + .axi_rsp_o (axi_rsp), + .mon_w_valid_o, + .mon_w_addr_o, + .mon_w_data_o, + .mon_w_id_o, + .mon_w_user_o, + .mon_w_beat_count_o, + .mon_w_last_o, + .mon_r_valid_o, + .mon_r_addr_o, + .mon_r_data_o, + .mon_r_id_o, + .mon_r_user_o, + .mon_r_beat_count_o, + .mon_r_last_o + ); + +endmodule + +/// Mutliport interface variant of [`axi_sim_mem`](module.axi_sim_mem). +/// +/// See the documentation of the main module for the definition of ports and parameters. +module axi_sim_mem_multiport_intf #( + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0, + parameter int unsigned NUM_PORTS = 32'd1, + parameter bit WARN_UNINITIALIZED = 1'b0, + parameter UNINITIALIZED_DATA = "undefined", + parameter bit ClearErrOnAccess = 1'b0, + parameter time APPL_DELAY = 0ps, + parameter time ACQ_DELAY = 0ps +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave axi_slv[NUM_PORTS], + output logic [NUM_PORTS-1:0] mon_w_valid_o, + output logic [NUM_PORTS-1:0][AXI_ADDR_WIDTH-1:0] mon_w_addr_o, + output logic [NUM_PORTS-1:0][AXI_DATA_WIDTH-1:0] mon_w_data_o, + output logic [NUM_PORTS-1:0][AXI_ID_WIDTH-1:0] mon_w_id_o, + output logic [NUM_PORTS-1:0][AXI_USER_WIDTH-1:0] mon_w_user_o, + output axi_pkg::len_t [NUM_PORTS-1:0] mon_w_beat_count_o, + output logic [NUM_PORTS-1:0] mon_w_last_o, + output logic [NUM_PORTS-1:0] mon_r_valid_o, + output logic [NUM_PORTS-1:0][AXI_ADDR_WIDTH-1:0] mon_r_addr_o, + output logic [NUM_PORTS-1:0][AXI_DATA_WIDTH-1:0] mon_r_data_o, + output logic [NUM_PORTS-1:0][AXI_ID_WIDTH-1:0] mon_r_id_o, + output logic [NUM_PORTS-1:0][AXI_USER_WIDTH-1:0] mon_r_user_o, + output axi_pkg::len_t [NUM_PORTS-1:0] mon_r_beat_count_o, + output logic [NUM_PORTS-1:0] mon_r_last_o +); + + typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t; + typedef logic [AXI_ID_WIDTH-1:0] axi_id_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t; + typedef logic [AXI_USER_WIDTH-1:0] axi_user_t; + `AXI_TYPEDEF_ALL(axi, axi_addr_t, axi_id_t, axi_data_t, axi_strb_t, axi_user_t) + + axi_req_t [NUM_PORTS-1:0] axi_req; + axi_resp_t [NUM_PORTS-1:0] axi_rsp; + + for (genvar i = 0; i < NUM_PORTS; i++) begin + `AXI_ASSIGN_TO_REQ(axi_req[i], axi_slv[i]) + `AXI_ASSIGN_FROM_RESP(axi_slv[i], axi_rsp[i]) + end + + axi_sim_mem #( + .AddrWidth (AXI_ADDR_WIDTH), + .DataWidth (AXI_DATA_WIDTH), + .IdWidth (AXI_ID_WIDTH), + .UserWidth (AXI_USER_WIDTH), + .NumPorts (NUM_PORTS), + .axi_req_t (axi_req_t), + .axi_rsp_t (axi_resp_t), + .WarnUninitialized (WARN_UNINITIALIZED), + .UninitializedData (UNINITIALIZED_DATA), + .ClearErrOnAccess (ClearErrOnAccess), + .ApplDelay (APPL_DELAY), + .AcqDelay (ACQ_DELAY) + ) i_sim_mem ( + .clk_i, + .rst_ni, + .axi_req_i (axi_req), + .axi_rsp_o (axi_rsp), + .mon_w_valid_o, + .mon_w_addr_o, + .mon_w_data_o, + .mon_w_id_o, + .mon_w_user_o, + .mon_w_beat_count_o, + .mon_w_last_o, + .mon_r_valid_o, + .mon_r_addr_o, + .mon_r_data_o, + .mon_r_id_o, + .mon_r_user_o, + .mon_r_beat_count_o, + .mon_r_last_o + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_slave_compare.sv b/hw/vendor/pulp_platform_axi/src/axi_slave_compare.sv new file mode 100644 index 000000000..6cda82e05 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_slave_compare.sv @@ -0,0 +1,191 @@ +// Copyright (c) 2019-2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +`include "axi/assign.svh" +/// Synthesizable test module comparing two AXI slaves of the same type. +/// The reference response is always passed to the master, whereas the test response +/// is discarded after handshaking. +/// This module is meant to be used in FPGA-based verification. +module axi_slave_compare #( + /// ID width of the AXI4+ATOP interface + parameter int unsigned AxiIdWidth = 32'd0, + /// FIFO depth + parameter int unsigned FifoDepth = 32'd0, + /// Consider size field in comparison + parameter bit UseSize = 1'b0, + /// Data width of the AXI4+ATOP interface + parameter int unsigned DataWidth = 32'd8, + /// AW channel type of the AXI4+ATOP interface + parameter type axi_aw_chan_t = logic, + /// W channel type of the AXI4+ATOP interface + parameter type axi_w_chan_t = logic, + /// B channel type of the AXI4+ATOP interface + parameter type axi_b_chan_t = logic, + /// AR channel type of the AXI4+ATOP interface + parameter type axi_ar_chan_t = logic, + /// R channel type of the AXI4+ATOP interface + parameter type axi_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP slave port + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type axi_rsp_t = logic, + /// ID type (*do not overwrite*) + parameter type id_t = logic [2**AxiIdWidth-1:0] +)( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4+ATOP channel request in + input axi_req_t axi_mst_req_i, + /// AXI4+ATOP channel response out + output axi_rsp_t axi_mst_rsp_o, + /// AXI4+ATOP reference channel request out + output axi_req_t axi_ref_req_o, + /// AXI4+ATOP reference channel response in + input axi_rsp_t axi_ref_rsp_i, + /// AXI4+ATOP test channel request out + output axi_req_t axi_test_req_o, + /// AXI4+ATOP test channel response in + input axi_rsp_t axi_test_rsp_i, + /// AW mismatch + output id_t aw_mismatch_o, + /// W mismatch + output logic w_mismatch_o, + /// B mismatch + output id_t b_mismatch_o, + /// AR mismatch + output id_t ar_mismatch_o, + /// R mismatch + output id_t r_mismatch_o, + /// General mismatch + output logic mismatch_o, + /// Unit is busy + output logic busy_o +); + + axi_req_t axi_ref_req_in, axi_test_req_in; + axi_rsp_t axi_ref_rsp_in, axi_test_rsp_in; + + logic aw_valid_ref, aw_ready_ref; + logic w_valid_ref, w_ready_ref; + logic ar_valid_ref, ar_ready_ref; + + logic aw_valid_test, aw_ready_test; + logic w_valid_test, w_ready_test; + logic ar_valid_test, ar_ready_test; + + logic aw_ready_mst; + logic w_ready_mst; + logic ar_ready_mst; + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.aw_valid ), + .ready_o ( aw_ready_mst ), + .valid_o ( { aw_valid_ref, aw_valid_test } ), + .ready_i ( { aw_ready_ref, aw_ready_test } ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.ar_valid ), + .ready_o ( ar_ready_mst ), + .valid_o ( { ar_valid_ref, ar_valid_test } ), + .ready_i ( { ar_ready_ref, ar_ready_test } ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.w_valid ), + .ready_o ( w_ready_mst ), + .valid_o ( { w_valid_ref, w_valid_test } ), + .ready_i ( { w_ready_ref, w_ready_test } ) + ); + + // assemble buses + always_comb begin + // request + `AXI_SET_REQ_STRUCT(axi_ref_req_in, axi_mst_req_i) + `AXI_SET_REQ_STRUCT(axi_test_req_in, axi_mst_req_i) + // overwrite valids in requests + axi_ref_req_in.aw_valid = aw_valid_ref; + axi_ref_req_in.ar_valid = ar_valid_ref; + axi_ref_req_in.w_valid = w_valid_ref; + axi_test_req_in.aw_valid = aw_valid_test; + axi_test_req_in.ar_valid = ar_valid_test; + axi_test_req_in.w_valid = w_valid_test; + // get readies + aw_ready_ref = axi_ref_rsp_in.aw_ready; + ar_ready_ref = axi_ref_rsp_in.ar_ready; + w_ready_ref = axi_ref_rsp_in.w_ready; + aw_ready_test = axi_test_rsp_in.aw_ready; + ar_ready_test = axi_test_rsp_in.ar_ready; + w_ready_test = axi_test_rsp_in.w_ready; + // response + `AXI_SET_RESP_STRUCT(axi_mst_rsp_o, axi_ref_rsp_in) + // overwrite readies + axi_mst_rsp_o.aw_ready = aw_ready_mst; + axi_mst_rsp_o.w_ready = w_ready_mst; + axi_mst_rsp_o.ar_ready = ar_ready_mst; + // b interface is not used + axi_test_req_in.r_ready = '1; + axi_test_req_in.b_ready = '1; + end + + axi_bus_compare #( + .AxiIdWidth ( AxiIdWidth ), + .FifoDepth ( FifoDepth ), + .UseSize ( UseSize ), + .DataWidth ( DataWidth ), + .axi_aw_chan_t ( axi_aw_chan_t ), + .axi_w_chan_t ( axi_w_chan_t ), + .axi_b_chan_t ( axi_b_chan_t ), + .axi_ar_chan_t ( axi_ar_chan_t ), + .axi_r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ) + ) i_axi_bus_compare ( + .clk_i, + .rst_ni, + .testmode_i, + .aw_mismatch_o, + .w_mismatch_o, + .b_mismatch_o, + .ar_mismatch_o, + .r_mismatch_o, + .mismatch_o, + .busy_o, + .axi_a_req_i ( axi_ref_req_in ), + .axi_a_rsp_o ( axi_ref_rsp_in ), + .axi_a_req_o ( axi_ref_req_o ), + .axi_a_rsp_i ( axi_ref_rsp_i ), + .axi_b_req_i ( axi_test_req_in ), + .axi_b_rsp_o ( axi_test_rsp_in ), + .axi_b_req_o ( axi_test_req_o ), + .axi_b_rsp_i ( axi_test_rsp_i ) + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_test.sv b/hw/vendor/pulp_platform_axi/src/axi_test.sv index 4fe92be75..ce8638343 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_test.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_test.sv @@ -10,10 +10,10 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Wolfgang Roenninger // - Andreas Kurth +// - Wolfgang Roenninger // - Fabian Schuiki -// - Florian Zaruba +// - Thomas Benz // - Matheus Cavalcante @@ -591,6 +591,92 @@ package axi_test; axi.r_ready <= #TA 0; endtask + /// Monitor the AW channel and return the next beat. + task mon_aw ( + output ax_beat_t beat + ); + cycle_start(); + while (!(axi.aw_valid && axi.aw_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.ax_id = axi.aw_id; + beat.ax_addr = axi.aw_addr; + beat.ax_len = axi.aw_len; + beat.ax_size = axi.aw_size; + beat.ax_burst = axi.aw_burst; + beat.ax_lock = axi.aw_lock; + beat.ax_cache = axi.aw_cache; + beat.ax_prot = axi.aw_prot; + beat.ax_qos = axi.aw_qos; + beat.ax_region = axi.aw_region; + beat.ax_atop = axi.aw_atop; + beat.ax_user = axi.aw_user; + cycle_end(); + endtask + + /// Monitor the W channel and return the next beat. + task mon_w ( + output w_beat_t beat + ); + cycle_start(); + while (!(axi.w_valid && axi.w_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.w_data = axi.w_data; + beat.w_strb = axi.w_strb; + beat.w_last = axi.w_last; + beat.w_user = axi.w_user; + cycle_end(); + endtask + + /// Monitor the B channel and return the next beat. + task mon_b ( + output b_beat_t beat + ); + cycle_start(); + while (!(axi.b_valid && axi.b_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.b_id = axi.b_id; + beat.b_resp = axi.b_resp; + beat.b_user = axi.b_user; + cycle_end(); + endtask + + /// Monitor the AR channel and return the next beat. + task mon_ar ( + output ax_beat_t beat + ); + cycle_start(); + while (!(axi.ar_valid && axi.ar_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.ax_id = axi.ar_id; + beat.ax_addr = axi.ar_addr; + beat.ax_len = axi.ar_len; + beat.ax_size = axi.ar_size; + beat.ax_burst = axi.ar_burst; + beat.ax_lock = axi.ar_lock; + beat.ax_cache = axi.ar_cache; + beat.ax_prot = axi.ar_prot; + beat.ax_qos = axi.ar_qos; + beat.ax_region = axi.ar_region; + beat.ax_atop = 'X; // Not defined on the AR channel. + beat.ax_user = axi.ar_user; + cycle_end(); + endtask + + /// Monitor the R channel and return the next beat. + task mon_r ( + output r_beat_t beat + ); + cycle_start(); + while (!(axi.r_valid && axi.r_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.r_id = axi.r_id; + beat.r_data = axi.r_data; + beat.r_resp = axi.r_resp; + beat.r_last = axi.r_last; + beat.r_user = axi.r_user; + cycle_end(); + endtask + endclass class axi_rand_master #( @@ -613,6 +699,7 @@ package axi_test; parameter int RESP_MIN_WAIT_CYCLES = 0, parameter int RESP_MAX_WAIT_CYCLES = 20, // AXI feature usage + parameter int SIZE_ALIGN = 0, parameter int AXI_MAX_BURST_LEN = 0, // maximum number of beats in burst; 0 = AXI max (256) parameter int TRAFFIC_SHAPING = 0, parameter bit AXI_EXCLS = 1'b0, @@ -675,6 +762,7 @@ package axi_test; struct packed { int unsigned len ; + int unsigned size ; int unsigned cprob; } traffic_shape[$]; int unsigned max_cprob; @@ -721,15 +809,29 @@ package axi_test; mem_map.push_back({addr_begin, addr_end, mem_type}); endfunction + function void clear_memory_regions(); + mem_map.delete(); + endfunction + function void add_traffic_shaping(input int unsigned len, input int unsigned freq); + int unsigned size = -1; if (traffic_shape.size() == 0) - traffic_shape.push_back({len, freq}); + traffic_shape.push_back({len, size, freq}); else - traffic_shape.push_back({len, traffic_shape[$].cprob + freq}); + traffic_shape.push_back({len, size, traffic_shape[$].cprob + freq}); max_cprob = traffic_shape[$].cprob; endfunction : add_traffic_shaping + function void add_traffic_shaping_with_size(input int unsigned len, input int unsigned size, input int unsigned freq); + if (traffic_shape.size() == 0) + traffic_shape.push_back({len, size, freq}); + else + traffic_shape.push_back({len, size, traffic_shape[$].cprob + freq}); + + max_cprob = traffic_shape[$].cprob; + endfunction : add_traffic_shaping_with_size + function ax_beat_t new_rand_burst(input logic is_read); automatic logic rand_success; automatic ax_beat_t ax_beat = new; @@ -776,18 +878,26 @@ package axi_test; for (int i = 0; i < traffic_shape.size(); i++) if (traffic_shape[i].cprob > cprob) begin len = traffic_shape[i].len; - assert (ax_beat.ax_burst == BURST_WRAP -> len inside {len_t'(1), len_t'(3), len_t'(7), len_t'(15)}); + size = traffic_shape[i].size; + if (ax_beat.ax_burst == BURST_WRAP) begin + assert (len inside {len_t'(1), len_t'(3), len_t'(7), len_t'(15)}); + end break; end // Randomize address. Make sure that the burst does not cross a 4KiB boundary. forever begin - rand_success = std::randomize(size) with { - 2**size <= AXI_STRB_WIDTH; - 2**size <= len; - }; assert(rand_success); - ax_beat.ax_size = size; - ax_beat.ax_len = ((len + (1 << size) - 1) >> size) - 1; + if(size==-1) begin + rand_success = std::randomize(size) with { + 2**size <= AXI_STRB_WIDTH; + 2**size <= len; + }; assert(rand_success); + ax_beat.ax_size = size; + ax_beat.ax_len = ((len + (1 << size) - 1) >> size) - 1; + end else begin + ax_beat.ax_size = size; + ax_beat.ax_len = len; + end rand_success = std::randomize(addr) with { addr >= mem_region.addr_begin; @@ -795,14 +905,9 @@ package axi_test; addr + len <= mem_region.addr_end; }; assert(rand_success); - if (ax_beat.ax_burst == axi_pkg::BURST_FIXED) begin - if (((addr + 2**ax_beat.ax_size) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end - end else begin // BURST_INCR - if (((addr + 2**ax_beat.ax_size * (ax_beat.ax_len + 1)) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end + if (axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, ax_beat.ax_len) >> 12) begin + break; end end end else begin @@ -827,19 +932,14 @@ package axi_test; addr + ((len + 1) << size) <= mem_region.addr_end; }; assert(rand_success); - if (ax_beat.ax_burst == axi_pkg::BURST_FIXED) begin - if (((addr + 2**ax_beat.ax_size) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end - end else begin // BURST_INCR, BURST_WRAP - if (((addr + 2**ax_beat.ax_size * (ax_beat.ax_len + 1)) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end + if (axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, ax_beat.ax_len) >> 12) begin + break; end end end - ax_beat.ax_addr = addr; + ax_beat.ax_addr = axi_pkg::aligned_addr(addr, axi_pkg::size_t'(SIZE_ALIGN) ); rand_success = std::randomize(id); assert(rand_success); rand_success = std::randomize(qos); assert(rand_success); // The random ID *must* be legalized with `legalize_id()` before the beat is sent! This is @@ -851,98 +951,116 @@ package axi_test; task rand_atop_burst(inout ax_beat_t beat); automatic logic rand_success; - beat.ax_atop[5:4] = $random(); - if (beat.ax_atop[5:4] != 2'b00 && !AXI_BURST_INCR) begin - // We can emit ATOPs only if INCR bursts are allowed. - $warning("ATOP suppressed because INCR bursts are disabled!"); - beat.ax_atop[5:4] = 2'b00; - end - if (beat.ax_atop[5:4] != 2'b00) begin // ATOP - // Determine `ax_atop`. - if (beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICSTORE || - beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICLOAD) begin - // Endianness - beat.ax_atop[3] = $random(); - // Atomic operation - beat.ax_atop[2:0] = $random(); - end else begin // Atomic{Swap,Compare} - beat.ax_atop[3:1] = '0; - beat.ax_atop[0] = $random(); + forever begin + beat.ax_atop[5:4] = $random(); + if (beat.ax_atop[5:4] != 2'b00 && !AXI_BURST_INCR) begin + // We can emit ATOPs only if INCR bursts are allowed. + $warning("ATOP suppressed because INCR bursts are disabled!"); + beat.ax_atop[5:4] = 2'b00; end - // Determine `ax_size` and `ax_len`. - if (2**beat.ax_size < AXI_STRB_WIDTH) begin - // Transaction does *not* occupy full data bus, so we must send just one beat. [E2.1.3] - beat.ax_len = '0; - end else begin - automatic int unsigned bytes; - if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin - // Total data transferred in burst can be 2, 4, 8, 16, or 32 B. - automatic int unsigned log_bytes; - rand_success = std::randomize(log_bytes) with { - log_bytes > 0; 2**log_bytes <= 32; - }; assert(rand_success); - bytes = 2**log_bytes; + if (beat.ax_atop[5:4] != 2'b00) begin // ATOP + // Determine `ax_atop`. + if (beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICSTORE || + beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICLOAD) begin + // Endianness + beat.ax_atop[3] = $random(); + // Atomic operation + beat.ax_atop[2:0] = $random(); + end else begin // Atomic{Swap,Compare} + beat.ax_atop[3:1] = '0; + beat.ax_atop[0] = $random(); + end + // Determine `ax_size` and `ax_len`. + if (2**beat.ax_size < AXI_STRB_WIDTH) begin + // Transaction does *not* occupy full data bus, so we must send just one beat. [E1.1.3] + beat.ax_len = '0; end else begin - // Total data transferred in burst can be 1, 2, 4, or 8 B. - if (AXI_STRB_WIDTH >= 8) begin - bytes = AXI_STRB_WIDTH; - end else begin + automatic int unsigned bytes; + if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin + // Total data transferred in burst can be 2, 4, 8, 16, or 32 B. automatic int unsigned log_bytes; - rand_success = std::randomize(log_bytes); assert(rand_success); - log_bytes = log_bytes % (4 - $clog2(AXI_STRB_WIDTH)) - $clog2(AXI_STRB_WIDTH); + rand_success = std::randomize(log_bytes) with { + log_bytes > 0; 2**log_bytes <= 32; + }; assert(rand_success); bytes = 2**log_bytes; + end else begin + // Total data transferred in burst can be 1, 2, 4, or 8 B. + if (AXI_STRB_WIDTH >= 8) begin + bytes = AXI_STRB_WIDTH; + end else begin + automatic int unsigned log_bytes; + rand_success = std::randomize(log_bytes); assert(rand_success); + log_bytes = log_bytes % (4 - $clog2(AXI_STRB_WIDTH)) - $clog2(AXI_STRB_WIDTH); + bytes = 2**log_bytes; + end end + beat.ax_len = bytes / AXI_STRB_WIDTH - 1; end - beat.ax_len = bytes / AXI_STRB_WIDTH - 1; - end - // Determine `ax_addr` and `ax_burst`. - if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin - // The address must be aligned to half the outbound data size. [E2-337] - beat.ax_addr = beat.ax_addr & ~((1'b1 << beat.ax_size) - 1); - // If the address is aligned to the total size of outgoing data, the burst type must be - // INCR. Otherwise, it must be WRAP. [E2-338] - beat.ax_burst = (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size) == 0) ? - axi_pkg::BURST_INCR : axi_pkg::BURST_WRAP; - // If we are not allowed to emit WRAP bursts, align the address to the total size of - // outgoing data and fall back to INCR. - if (beat.ax_burst == axi_pkg::BURST_WRAP && !AXI_BURST_WRAP) begin - beat.ax_addr -= (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size)); + // Determine `ax_addr` and `ax_burst`. + if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin + // The address must be aligned to half the outbound data size. [E1.1.3] + beat.ax_addr = beat.ax_addr & ~((1'b1 << beat.ax_size) - 1); + // If the address is aligned to the total size of outgoing data, the burst type must be + // INCR. Otherwise, it must be WRAP. [E1.1.3] + beat.ax_burst = (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size) == 0) ? + axi_pkg::BURST_INCR : axi_pkg::BURST_WRAP; + // If we are not allowed to emit WRAP bursts, align the address to the total size of + // outgoing data and fall back to INCR. + if (beat.ax_burst == axi_pkg::BURST_WRAP && !AXI_BURST_WRAP) begin + beat.ax_addr -= (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size)); + beat.ax_burst = axi_pkg::BURST_INCR; + end + end else begin + // The address must be aligned to the data size. [E1.1.3] + beat.ax_addr = beat.ax_addr & ~((1'b1 << (beat.ax_size+1)) - 1); + // Only INCR allowed. beat.ax_burst = axi_pkg::BURST_INCR; end - end else begin - // The address must be aligned to the data size. [E2-337] - beat.ax_addr = beat.ax_addr & ~((1'b1 << (beat.ax_size+1)) - 1); - // Only INCR allowed. - beat.ax_burst = axi_pkg::BURST_INCR; + // Make sure that the burst does not cross a 4KiB boundary. + if (axi_pkg::beat_addr(beat.ax_addr, beat.ax_size, beat.ax_len, beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(beat.ax_addr, beat.ax_size, beat.ax_len, beat.ax_burst, beat.ax_len) >> 12) begin + break; + end else begin + beat = new_rand_burst(1'b0); + end end end endtask function void rand_excl_ar(inout ax_beat_t ar_beat); - ar_beat.ax_lock = $random(); - if (ar_beat.ax_lock) begin - automatic logic rand_success; - automatic int unsigned n_bytes; - automatic size_t size; - automatic addr_t addr_mask; - // In an exclusive burst, the number of bytes to be transferred must be a power of 2, i.e., - // 1, 2, 4, 8, 16, 32, 64, or 128 bytes, and the burst length must not exceed 16 transfers. - static int unsigned ul = (AXI_STRB_WIDTH < 8) ? 4 + $clog2(AXI_STRB_WIDTH) : 7; - rand_success = std::randomize(n_bytes) with { - n_bytes >= 1; - n_bytes <= ul; - }; assert(rand_success); - n_bytes = 2**n_bytes; - rand_success = std::randomize(size) with { - size >= 0; - 2**size <= n_bytes; - 2**size <= AXI_STRB_WIDTH; - n_bytes / 2**size <= 16; - }; assert(rand_success); - ar_beat.ax_size = size; - ar_beat.ax_len = n_bytes / 2**size; - // The address must be aligned to the total number of bytes in the burst. - ar_beat.ax_addr = ar_beat.ax_addr & ~(n_bytes-1); + forever begin + ar_beat.ax_lock = $random(); + if (ar_beat.ax_lock) begin + automatic logic rand_success; + automatic int unsigned n_bytes; + automatic size_t size; + automatic addr_t addr_mask; + // In an exclusive burst, the number of bytes to be transferred must be a power of 2, i.e., + // 1, 2, 4, 8, 16, 32, 64, or 128 bytes, and the burst length must not exceed 16 transfers. + static int unsigned ul = (AXI_STRB_WIDTH < 8) ? 4 + $clog2(AXI_STRB_WIDTH) : 7; + rand_success = std::randomize(n_bytes) with { + n_bytes >= 1; + n_bytes <= ul; + }; assert(rand_success); + n_bytes = 2**n_bytes; + rand_success = std::randomize(size) with { + size >= 0; + 2**size <= n_bytes; + 2**size <= AXI_STRB_WIDTH; + n_bytes / 2**size <= 16; + }; assert(rand_success); + ar_beat.ax_size = size; + ar_beat.ax_len = n_bytes / 2**size; + // The address must be aligned to the total number of bytes in the burst. + ar_beat.ax_addr = ar_beat.ax_addr & ~(n_bytes-1); + end + // Make sure that the burst does not cross a 4KiB boundary. + if (axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, ar_beat.ax_len) >> 12) begin + break; + end else begin + ar_beat = new_rand_burst(1'b1); + end end endfunction @@ -1011,7 +1129,7 @@ package axi_test; if (beat.ax_atop != 2'b00) begin // This is an ATOP, so it gives rise to a write response. atop_resp_b[beat.ax_id] = 1'b1; - if (beat.ax_atop[5]) begin + if (beat.ax_atop[axi_pkg::ATOP_R_RESP]) begin // This ATOP type additionally gives rise to a read response. atop_resp_r[beat.ax_id] = 1'b1; end @@ -1060,6 +1178,8 @@ package axi_test; end cnt_sem.put(); end + end else begin + rand_wait(1,1); end end endtask @@ -1102,12 +1222,19 @@ package axi_test; static logic rand_success; wait (w_queue.size() > 0 || (aw_done && w_queue.size() == 0)); aw_beat = w_queue.pop_front(); - addr = aw_beat.ax_addr; for (int unsigned i = 0; i < aw_beat.ax_len + 1; i++) begin automatic w_beat_t w_beat = new; automatic int unsigned begin_byte, end_byte, n_bytes; automatic logic [AXI_STRB_WIDTH-1:0] rand_strb, strb_mask; + addr = axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, + aw_beat.ax_burst, i); +`ifdef XSIM + // std::randomize(w_beat) may behave differently to w_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(w_beat); assert (rand_success); +`else rand_success = w_beat.randomize(); assert (rand_success); +`endif // Determine strobe. w_beat.w_strb = '0; n_bytes = 2**aw_beat.ax_size; @@ -1122,8 +1249,6 @@ package axi_test; w_beat.w_last = (i == aw_beat.ax_len); rand_wait(W_MIN_WAIT_CYCLES, W_MAX_WAIT_CYCLES); drv.send_w(w_beat); - if (aw_beat.ax_burst == axi_pkg::BURST_INCR) - addr += n_bytes; end end endtask @@ -1181,7 +1306,11 @@ package axi_test; parameter int R_MIN_WAIT_CYCLES = 0, parameter int R_MAX_WAIT_CYCLES = 5, parameter int RESP_MIN_WAIT_CYCLES = 0, - parameter int RESP_MAX_WAIT_CYCLES = 20 + parameter int RESP_MAX_WAIT_CYCLES = 20, + /// This parameter eneables an internal memory, which gets randomly initialized, if it is read + /// and retains written data. This mode does currently not support `axi_pkg::BURST_WRAP`! + /// All responses are `axi_pkg::RESP_OKAY` when in this mode. + parameter bit MAPPED = 1'b0 ); typedef axi_test::axi_driver #( .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TA), .TT(TT) @@ -1195,11 +1324,17 @@ package axi_test; typedef axi_driver_t::r_beat_t r_beat_t; typedef axi_driver_t::w_beat_t w_beat_t; + typedef logic [AW-1:0] addr_t; + typedef logic [7:0] byte_t; + axi_driver_t drv; rand_ax_beat_queue_t ar_queue; ax_beat_t aw_queue[$]; int unsigned b_wait_cnt; + // Memory array for when the `MAPPED` parameter is set. + byte_t memory_q[addr_t]; + function new( virtual AXI_BUS_DV #( .AXI_ADDR_WIDTH(AW), @@ -1215,7 +1350,8 @@ package axi_test; endfunction function void reset(); - drv.reset_slave(); + this.drv.reset_slave(); + this.memory_q.delete(); endfunction // TODO: The `rand_wait` task exists in `rand_verif_pkg`, but that task cannot be called with @@ -1235,6 +1371,10 @@ package axi_test; automatic ax_beat_t ar_beat; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); drv.recv_ar(ar_beat); + if (MAPPED) begin + assert (ar_beat.ax_burst != axi_pkg::BURST_WRAP) else + $error("axi_pkg::BURST_WRAP not supported in MAPPED mode."); + end ar_queue.push(ar_beat.ax_id, ar_beat); end endtask @@ -1243,10 +1383,30 @@ package axi_test; forever begin automatic logic rand_success; automatic ax_beat_t ar_beat; - automatic r_beat_t r_beat = new; - wait (!ar_queue.empty()); - ar_beat = ar_queue.peek(); + automatic r_beat_t r_beat = new; + automatic addr_t byte_addr; + wait (ar_queue.size > 0); + ar_beat = ar_queue.peek(); + byte_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, axi_pkg::size_t'($clog2(DW/8))); +`ifdef XSIM + // std::randomize(r_beat) may behave differently to r_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(r_beat); assert(rand_success); +`else rand_success = r_beat.randomize(); assert(rand_success); +`endif + if (MAPPED) begin + // Either use the actual data, or save the random generated. + for (int unsigned i = 0; i < (DW/8); i++) begin + if (this.memory_q.exists(byte_addr)) begin + r_beat.r_data[i*8+:8] = this.memory_q[byte_addr]; + end else begin + this.memory_q[byte_addr] = r_beat.r_data[i*8+:8]; + end + byte_addr++; + end + r_beat.r_resp = axi_pkg::RESP_OKAY; + end r_beat.r_id = ar_beat.ax_id; if (RAND_RESP && !ar_beat.ax_atop[axi_pkg::ATOP_R_RESP]) r_beat.r_resp[1] = $random(); @@ -1257,6 +1417,10 @@ package axi_test; r_beat.r_last = 1'b1; void'(ar_queue.pop_id(ar_beat.ax_id)); end else begin + if ((ar_beat.ax_burst == axi_pkg::BURST_INCR) && MAPPED) begin + ar_beat.ax_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, ar_beat.ax_size) + + 2**ar_beat.ax_size; + end ar_beat.ax_len--; ar_queue.set(ar_beat.ax_id, ar_beat); end @@ -1269,9 +1433,15 @@ package axi_test; automatic ax_beat_t aw_beat; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); drv.recv_aw(aw_beat); + if (MAPPED) begin + assert (aw_beat.ax_atop == '0) else + $error("ATOP not supported in MAPPED mode."); + assert (aw_beat.ax_burst != axi_pkg::BURST_WRAP) else + $error("axi_pkg::BURST_WRAP not supported in MAPPED mode."); + end aw_queue.push_back(aw_beat); // Atomic{Load,Swap,Compare}s require an R response. - if (aw_beat.ax_atop[5]) begin + if (aw_beat.ax_atop[axi_pkg::ATOP_R_RESP]) begin ar_queue.push(aw_beat.ax_id, aw_beat); end end @@ -1280,10 +1450,30 @@ package axi_test; task recv_ws(); forever begin automatic ax_beat_t aw_beat; + automatic addr_t byte_addr; forever begin automatic w_beat_t w_beat; rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); drv.recv_w(w_beat); + if (MAPPED) begin + wait (aw_queue.size() > 0); + aw_beat = aw_queue[0]; + byte_addr = axi_pkg::aligned_addr(aw_beat.ax_addr, $clog2(DW/8)); + + // Write Data if the strobe is defined + for (int unsigned i = 0; i < (DW/8); i++) begin + if (w_beat.w_strb[i]) begin + this.memory_q[byte_addr] = w_beat.w_data[i*8+:8]; + end + byte_addr++; + end + // Update address in beat + if (aw_beat.ax_burst == axi_pkg::BURST_INCR) begin + aw_beat.ax_addr = axi_pkg::aligned_addr(aw_beat.ax_addr, aw_beat.ax_size) + + 2**aw_beat.ax_size; + end + aw_queue[0] = aw_beat; + end if (w_beat.w_last) break; end @@ -1298,7 +1488,13 @@ package axi_test; automatic logic rand_success; wait (b_wait_cnt > 0 && (aw_queue.size() != 0)); aw_beat = aw_queue.pop_front(); - rand_success = b_beat.randomize(); assert(rand_success); +`ifdef XSIM + // std::randomize(b_beat) may behave differently to b_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(b_beat); assert (rand_success); +`else + rand_success = b_beat.randomize(); assert (rand_success); +`endif b_beat.b_id = aw_beat.ax_id; if (RAND_RESP && !aw_beat.ax_atop[axi_pkg::ATOP_R_RESP]) b_beat.b_resp[1] = $random(); @@ -1306,6 +1502,9 @@ package axi_test; b_beat.b_resp[0]= $random(); end rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); + if (MAPPED) begin + b_beat.b_resp = axi_pkg::RESP_OKAY; + end drv.send_b(b_beat); b_wait_cnt--; end @@ -1394,7 +1593,7 @@ package axi_test; ar_addr = addr_t'($urandom_range(MIN_ADDR, MAX_ADDR)); ar_prot = prot_t'($urandom()); this.ar_queue.push_back(ar_addr); - $display("%0t %s> Send AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); + // $display("%0t %s> Send AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); drv.send_ar(ar_addr, ar_prot); end endtask : send_ars @@ -1408,7 +1607,7 @@ package axi_test; ar_addr = this.ar_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); drv.recv_r(r_data, r_resp); - $display("%0t %s> Recv R with DATA: %h RESP: %0h", $time(), this.name, r_data, r_resp); + // $display("%0t %s> Recv R with DATA: %h RESP: %0h", $time(), this.name, r_data, r_resp); end endtask : recv_rs @@ -1420,7 +1619,7 @@ package axi_test; aw_addr = addr_t'($urandom_range(MIN_ADDR, MAX_ADDR)); aw_prot = prot_t'($urandom()); this.aw_queue.push_back(aw_addr); - $display("%0t %s> Send AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); + // $display("%0t %s> Send AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); this.drv.send_aw(aw_addr, aw_prot); this.b_queue.push_back(1'b1); end @@ -1437,7 +1636,7 @@ package axi_test; aw_addr = aw_queue.pop_front(); rand_success = std::randomize(w_data); assert(rand_success); rand_success = std::randomize(w_strb); assert(rand_success); - $display("%0t %s> Send W with DATA: %h STRB: %h", $time(), this.name, w_data, w_strb); + // $display("%0t %s> Send W with DATA: %h STRB: %h", $time(), this.name, w_data, w_strb); this.drv.send_w(w_data, w_strb); w_queue.push_back(1'b1); end @@ -1452,7 +1651,7 @@ package axi_test; go_b = this.w_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); this.drv.recv_b(b_resp); - $display("%0t %s> Recv B with RESP: %h", $time(), this.name, b_resp); + // $display("%0t %s> Recv B with RESP: %h", $time(), this.name, b_resp); end endtask : recv_bs @@ -1470,26 +1669,26 @@ package axi_test; // write data to a specific address task automatic write(input addr_t w_addr, input prot_t w_prot = prot_t'(0), input data_t w_data, input strb_t w_strb, output axi_pkg::resp_t b_resp); - $display("%0t %s> Write to ADDR: %h, PROT: %b DATA: %h, STRB: %h", - $time(), this.name, w_addr, w_prot, w_data, w_strb); + // $display("%0t %s> Write to ADDR: %h, PROT: %b DATA: %h, STRB: %h", + // $time(), this.name, w_addr, w_prot, w_data, w_strb); fork this.drv.send_aw(w_addr, w_prot); this.drv.send_w(w_data, w_strb); join this.drv.recv_b(b_resp); - $display("%0t %s> Received write response from ADDR: %h RESP: %h", - $time(), this.name, w_addr, b_resp); + // $display("%0t %s> Received write response from ADDR: %h RESP: %h", + // $time(), this.name, w_addr, b_resp); endtask : write // read data from a specific location task automatic read(input addr_t r_addr, input prot_t r_prot = prot_t'(0), output data_t r_data, output axi_pkg::resp_t r_resp); - $display("%0t %s> Read from ADDR: %h PROT: %b", - $time(), this.name, r_addr, r_prot); + // $display("%0t %s> Read from ADDR: %h PROT: %b", + // $time(), this.name, r_addr, r_prot); this.drv.send_ar(r_addr, r_prot); this.drv.recv_r(r_data, r_resp); - $display("%0t %s> Recieved read response from ADDR: %h DATA: %h RESP: %h", - $time(), this.name, r_addr, r_data, r_resp); + // $display("%0t %s> Recieved read response from ADDR: %h DATA: %h RESP: %h", + // $time(), this.name, r_addr, r_data, r_resp); endtask : read endclass @@ -1555,22 +1754,24 @@ package axi_test; automatic prot_t ar_prot; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); this.drv.recv_ar(ar_addr, ar_prot); - $display("%0t %s> Recv AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); + // $display("%0t %s> Recv AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); this.ar_queue.push_back(ar_addr); end endtask : recv_ars task automatic send_rs(); forever begin - automatic logic rand_success; - automatic addr_t ar_addr; - automatic data_t r_data; + automatic logic rand_success; + automatic addr_t ar_addr; + automatic data_t r_data; + automatic axi_pkg::resp_t r_resp; wait (ar_queue.size() > 0); ar_addr = this.ar_queue.pop_front(); rand_success = std::randomize(r_data); assert(rand_success); + rand_success = std::randomize(r_resp); assert(rand_success); rand_wait(R_MIN_WAIT_CYCLES, R_MAX_WAIT_CYCLES); - $display("%0t %s> Send R with DATA: %h", $time(), this.name, r_data); - this.drv.send_r(r_data, axi_pkg::RESP_OKAY); + // $display("%0t %s> Send R with DATA: %h RESP: %h", $time(), this.name, r_data, r_resp); + this.drv.send_r(r_data, r_resp); end endtask : send_rs @@ -1580,7 +1781,7 @@ package axi_test; automatic prot_t aw_prot; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); this.drv.recv_aw(aw_addr, aw_prot); - $display("%0t %s> Recv AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); + // $display("%0t %s> Recv AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); this.aw_queue.push_back(aw_addr); end endtask : recv_aws @@ -1591,7 +1792,7 @@ package axi_test; automatic strb_t w_strb; rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); this.drv.recv_w(w_data, w_strb); - $display("%0t %s> Recv W with DATA: %h SRTB: %h", $time(), this.name, w_data, w_strb); + // $display("%0t %s> Recv W with DATA: %h SRTB: %h", $time(), this.name, w_data, w_strb); this.b_queue.push_back(1'b1); end endtask : recv_ws @@ -1607,7 +1808,7 @@ package axi_test; go_b = this.b_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); rand_success = std::randomize(b_resp); assert(rand_success); - $display("%0t %s> Send B with RESP: %h", $time(), this.name, b_resp); + // $display("%0t %s> Send B with RESP: %h", $time(), this.name, b_resp); this.drv.send_b(b_resp); end endtask : send_bs @@ -1623,6 +1824,80 @@ package axi_test; endtask endclass + /// AXI Monitor. + class axi_monitor #( + /// AXI4+ATOP ID width + parameter int unsigned IW = 0, + /// AXI4+ATOP address width + parameter int unsigned AW = 0, + /// AXI4+ATOP data width + parameter int unsigned DW = 0, + /// AXI4+ATOP user width + parameter int unsigned UW = 0, + /// Stimuli test time + parameter time TT = 0ns + ); + + typedef axi_test::axi_driver #( + .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TT), .TT(TT) + ) axi_driver_t; + + typedef axi_driver_t::ax_beat_t ax_beat_t; + typedef axi_driver_t::w_beat_t w_beat_t; + typedef axi_driver_t::b_beat_t b_beat_t; + typedef axi_driver_t::r_beat_t r_beat_t; + + axi_driver_t drv; + mailbox aw_mbx = new, w_mbx = new, b_mbx = new, + ar_mbx = new, r_mbx = new; + + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AW), + .AXI_DATA_WIDTH(DW), + .AXI_ID_WIDTH(IW), + .AXI_USER_WIDTH(UW) + ) axi + ); + this.drv = new(axi); + endfunction + + task monitor; + fork + // AW + forever begin + automatic ax_beat_t ax; + this.drv.mon_aw(ax); + aw_mbx.put(ax); + end + // W + forever begin + automatic w_beat_t w; + this.drv.mon_w(w); + w_mbx.put(w); + end + // B + forever begin + automatic b_beat_t b; + this.drv.mon_b(b); + b_mbx.put(b); + end + // AR + forever begin + automatic ax_beat_t ax; + this.drv.mon_ar(ax); + ar_mbx.put(ax); + end + // R + forever begin + automatic r_beat_t r; + this.drv.mon_r(r); + r_mbx.put(r); + end + join + endtask + endclass + /// `axi_scoreboard` models a memory that only gets changed by the monitored AXI4+ATOP bus. /// /// This class is only capable of modeling `INCR` burst type, and cannot handle atomic operations. @@ -1647,15 +1922,15 @@ package axi_test; /// end class axi_scoreboard #( /// AXI4+ATOP ID width - parameter int unsigned IW, + parameter int unsigned IW = 0, /// AXI4+ATOP address width - parameter int unsigned AW, + parameter int unsigned AW = 0, /// AXI4+ATOP data width - parameter int unsigned DW, + parameter int unsigned DW = 0, /// AXI4+ATOP user width - parameter int unsigned UW, + parameter int unsigned UW = 0, /// Stimuli test time - parameter time TT + parameter time TT = 0ns ); // Number of checks localparam int unsigned NUM_CHECKS = 32'd3; @@ -1777,8 +2052,6 @@ package axi_test; b_beat = b_sample[id].pop_front(); if (check_en[BRespCheck]) begin assert (b_beat.b_id == id); - assert (b_beat.b_resp == axi_pkg::RESP_OKAY) else - $warning("Behavior for b_resp != axi_pkg::RESP_OKAY not modeled."); end // pop all accessed memory locations by this beat for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin @@ -1786,7 +2059,11 @@ package axi_test; axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, i), BUS_SIZE); for (int j = 0; j < axi_pkg::num_bytes(BUS_SIZE); j++) begin - memory_q[bus_address+j].delete(0); + if (b_beat.b_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY}) begin + memory_q[bus_address+j].delete(0); + end else begin + memory_q[bus_address+j].delete(memory_q[bus_address+j].size() - 1); + end end end end @@ -1800,6 +2077,7 @@ package axi_test; byte_t act_data; byte_t exp_data[$]; byte_t tst_data[$]; + int first_byte_to_check; forever begin wait (this.ar_sample[id].size() > 0); ar_beat = this.ar_sample[id].pop_front(); @@ -1814,28 +2092,39 @@ package axi_test; ar_beat.ax_burst, i); beat_address = axi_pkg::aligned_addr(beat_address, ar_beat.ax_size); bus_address = axi_pkg::aligned_addr(beat_address, BUS_SIZE); + if(i!=0) + first_byte_to_check = 0; + else + first_byte_to_check = ar_beat.ax_addr - beat_address; if (!this.memory_q.exists(bus_address)) begin for (int unsigned j = 0; j < axi_pkg::num_bytes(BUS_SIZE); j++) begin this.memory_q[bus_address+j].push_back(8'bxxxxxxxx); end end // Assert that the correct data is read. - if (this.check_en[ReadCheck]) begin - for (int unsigned j = 0; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin + if (this.check_en[ReadCheck] && + (r_beat.r_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY})) begin + for (int unsigned j = first_byte_to_check; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin idx_data = 8*BUS_SIZE'(beat_address+j); act_data = r_beat.r_data[idx_data+:8]; exp_data = this.memory_q[beat_address+j]; - tst_data = exp_data.find with (item === 8'hxx || item === act_data); - assert (tst_data.size() > 0) else begin - $warning("Unexpected RData ID: %0h Addr: %0h Byte Idx: %0h Exp Data : %0h Data: %h", - r_beat.r_id, beat_address+j, idx_data, exp_data, act_data); + if (exp_data.size() > 0) begin + tst_data = exp_data.find with (item === 8'hxx || item === act_data); + assert (tst_data.size() > 0) else begin + $warning("Unexpected RData ID: %0h \n \ + Addr: %h \n \ + Byte Idx: %h \n \ + Exp Data: %h \n \ + Act Data: %h \n \ + BeatData: %h", + r_beat.r_id, beat_address+j, idx_data, exp_data, act_data, r_beat.r_data); + end end end end end if (this.check_en[RRespCheck]) begin assert (r_beat.r_id == id); - assert (r_beat.r_resp == axi_pkg::RESP_OKAY); assert (r_beat.r_last); end end @@ -2020,8 +2309,267 @@ package axi_test; assert(this.b_queue[i].size() == 0); end endtask : reset + + /// Check that the byte in memory_q is the same as check_data. + task automatic check_byte(axi_addr_t check_addr, byte_t check_data); + assert(this.memory_q[check_addr][0] === check_data) else + $warning("Byte at ADDR: %h does not match: memory_q: %h check_data: %h", + check_addr, this.memory_q[check_addr][0], check_data); + endtask : check_byte + + /// Clear a byte from memoy. Can be used to partially delete mem space. + task clear_byte(axi_addr_t clear_addr); + if (this.memory_q.exists(clear_addr)) begin + this.memory_q.delete(clear_addr); + end + endtask : clear_byte + + /// Clear a memory range. + /// The end address alo gets cleared. + task automatic clear_range(axi_addr_t clear_start_addr, clear_end_addr); + axi_addr_t curr_addr = clear_start_addr; + while (curr_addr <= clear_end_addr) begin + this.clear_byte(curr_addr); + curr_addr++; + end + endtask : clear_range + + /// Get a byte from the modeled memory. + task automatic get_byte(input axi_addr_t byte_addr, output byte_t byte_data); + if (this.memory_q.exists(byte_addr)) begin + byte_data = this.memory_q[byte_addr][0]; + end else begin + byte_data = 8'hxx; + end + endtask : get_byte + endclass : axi_scoreboard + + class axi_file_master #( + // AXI interface parameters + parameter int AW = 32, + parameter int DW = 32, + parameter int IW = 8, + parameter int UW = 1, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps + ); + + typedef axi_test::axi_driver #( + .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TA), .TT(TT) + ) axi_driver_t; + + typedef axi_driver_t::ax_beat_t ax_beat_t; + typedef axi_driver_t::b_beat_t b_beat_t; + typedef axi_driver_t::r_beat_t r_beat_t; + typedef axi_driver_t::w_beat_t w_beat_t; + + axi_driver_t drv; + + int read_fd, write_fd; + + // store holding read/write transactions between aw-b and ar-r + logic b_outst[$]; + logic r_outst[$]; + + // proper decoupling: populate queues from file + ax_beat_t aw_queue[$]; + w_beat_t w_queue[$]; + ax_beat_t ar_queue[$]; + + // populated by read file function + int num_reads; + int num_writes; + + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AW), + .AXI_DATA_WIDTH(DW), + .AXI_ID_WIDTH(IW), + .AXI_USER_WIDTH(UW) + ) axi + ); + this.drv = new(axi); + this.reset(); + endfunction + + function void reset(); + drv.reset_master(); + endfunction + + function void parse_write(); + // parsing works + int parse_ok; + + // populate according to file + while (!$feof(this.write_fd)) begin + automatic ax_beat_t current_aw = new; + parse_ok = 1; + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_id ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "0x%x\n", current_aw.ax_addr ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_len ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_size ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_burst ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_lock ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_cache ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_prot ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_qos ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_region) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_atop ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_user ) != -1); + if (parse_ok) begin + this.aw_queue.push_back(current_aw); + this.b_outst.push_back(1'b1); + // $display("%p", current_aw); + end else begin + $warning("Issue parsing AW: %p", current_aw); + end + + // get write data + strobe + for (int i = 0; i <= current_aw.ax_len; i++) begin + automatic w_beat_t current_w = new; + parse_ok = parse_ok & ($fscanf(this.write_fd, "0x%x 0x%x %d\n", current_w.w_data, current_w.w_strb, current_w.w_user) != -1); + current_w.w_last = 1'b0; + if (i == current_aw.ax_len) begin + current_w.w_last = 1'b1; + end + if (parse_ok) begin + this.w_queue.push_back(current_w); + // $display("%p", current_w); + end else begin + $warning("Issue parsing W: %p of AW: %p", current_w, current_aw); + end + end + end + + // debug: print queues + // $display("%p", this.aw_queue); + // $display("%p", this.w_queue); + endfunction + + function void parse_read(); + // parsing works + int parse_ok; + + // populate according to file + while (!$feof(this.read_fd)) begin + automatic ax_beat_t current_ar = new; + parse_ok = 1; + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_id ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "0x%x\n", current_ar.ax_addr ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_len ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_size ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_burst ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_lock ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_cache ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_prot ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_qos ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_region) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_user ) != -1); + if (parse_ok) begin + this.ar_queue.push_back(current_ar); + this.r_outst.push_back(1'b1); + // $display("%p", current_ar); + end else begin + $warning("Issue parsing AR: %p", current_ar); + end + end + + // debug: print queues + // $display("%p", this.ar_queue); + endfunction + + function void load_files( + string read_file_name, + string write_file_name + ); + this.read_fd = $fopen(read_file_name, "r"); + this.write_fd = $fopen(write_file_name, "r"); + + // check if files are opened + if (this.read_fd) begin + $info("File %s opened successfully as %d", read_file_name, this.read_fd); + end else begin + $fatal(1, "File %s not found", read_file_name); + end + if (this.write_fd) begin + $info("File %s opened successfully as %d", write_file_name, this.write_fd); + end else begin + $fatal(1, "File %s not found", write_file_name); + end + + // read files + this.parse_read(); + this.parse_write(); + + // update status + this.num_reads = this.ar_queue.size(); + this.num_writes = this.aw_queue.size(); + endfunction + + task run_aw(); + // send aws while there are some left + while (this.aw_queue.size() > 0) begin + // display("Sending AW: %p", this.aw_queue[0]); + drv.send_aw(this.aw_queue[0]); + void'(this.aw_queue.pop_front()); + end + endtask + + task run_w(); + // send ws while there are some left + while (this.w_queue.size() > 0) begin + // $display("Sending W: %p", this.w_queue[0]); + drv.send_w(this.w_queue[0]); + void'(this.w_queue.pop_front()); + end + endtask + + task run_ar(); + // send ars while there are some left + while (this.ar_queue.size() > 0) begin + // $display("Sending AR: %p", this.ar_queue[0]); + drv.send_ar(this.ar_queue[0]); + void'(this.ar_queue.pop_front()); + end + endtask + + task wait_b(); + automatic b_beat_t b_beat = new; + // wait for bs while there are some left + while (this.b_outst.size() > 0) begin + // $display("Waiting B"); + drv.recv_b(b_beat); + void'(this.b_outst.pop_front()); + end + endtask + + task wait_r(); + automatic r_beat_t r_beat = new; + // wait for rs while there are some left + while (this.r_outst.size() > 0) begin + // $display("Waiting R"); + do begin + drv.recv_r(r_beat); + end while (r_beat.r_last !== 1'b1); + void'(this.r_outst.pop_front()); + end + endtask + + task run(); + fork + this.run_aw(); + this.run_w (); + this.run_ar(); + this.wait_b(); + this.wait_r(); + join + endtask + + endclass + endpackage // non synthesisable axi logger module @@ -2095,7 +2643,7 @@ module axi_chan_logger #( end // inject AR into queue, if there is an atomic - if (aw_chan_i.atop[5]) begin + if (aw_chan_i.atop[axi_pkg::ATOP_R_RESP]) begin $display("Atomic detected with response"); ar_beat.id = aw_chan_i.id; ar_beat.addr = aw_chan_i.addr; diff --git a/hw/vendor/pulp_platform_axi/src/axi_throttle.sv b/hw/vendor/pulp_platform_axi/src/axi_throttle.sv new file mode 100644 index 000000000..3e7973a08 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_throttle.sv @@ -0,0 +1,102 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Authors: +// - Thomas Benz + +/// Throttles an AXI4+ATOP bus. The maximum number of outstanding transfers have to +/// be set as a compile-time parameter, whereas the number of outstanding transfers can be set +/// during runtime. This module assumes either in-order processing of the requests or +/// indistinguishability of the request/responses (all ARs and AWs have the same ID respectively). +module axi_throttle #( + /// The maximum amount of allowable outstanding write requests + parameter int unsigned MaxNumAwPending = 1, + /// The maximum amount of allowable outstanding read requests + parameter int unsigned MaxNumArPending = 1, + /// AXI4+ATOP request type + parameter type axi_req_t = logic, + /// AXI4+ATOP response type + parameter type axi_rsp_t = logic, + /// The width of the write credit counter (*DO NOT OVERWRITE*) + parameter int unsigned WCntWidth = cf_math_pkg::idx_width(MaxNumAwPending), + /// The width of the read credit counter (*DO NOT OVERWRITE*) + parameter int unsigned RCntWidth = cf_math_pkg::idx_width(MaxNumArPending), + /// The type of the write credit counter (*DO NOT OVERWRITE*) + parameter type w_credit_t = logic [WCntWidth-1:0], + /// The type of the read credit counter (*DO NOT OVERWRITE*) + parameter type r_credit_t = logic [RCntWidth-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + + /// AXI4+ATOP request in + input axi_req_t req_i, + /// AXI4+ATOP response out + output axi_rsp_t rsp_o, + /// AXI4+ATOP request out + output axi_req_t req_o, + /// AXI4+ATOP response in + input axi_rsp_t rsp_i, + + /// Amount of write credit (number of outstanding write transfers) + input w_credit_t w_credit_i, + /// Amount of read credit (number of outstanding read transfers) + input r_credit_t r_credit_i +); + + // ax throttled valids + logic throttled_aw_valid; + logic throttled_ar_valid; + + // ax throttled readies + logic throttled_aw_ready; + logic throttled_ar_ready; + + // limit Aw requests -> wait for b + stream_throttle #( + .MaxNumPending ( MaxNumAwPending ) + ) i_stream_throttle_aw ( + .clk_i, + .rst_ni, + .req_valid_i ( req_i.aw_valid ), + .req_valid_o ( throttled_aw_valid ), + .req_ready_i ( rsp_i.aw_ready ), + .req_ready_o ( throttled_aw_ready ), + .rsp_valid_i ( rsp_i.b_valid ), + .rsp_ready_i ( req_i.b_ready ), + .credit_i ( w_credit_i ) + ); + + // limit Ar requests -> wait for r.last + stream_throttle #( + .MaxNumPending ( MaxNumArPending ) + ) i_stream_throttle_ar ( + .clk_i, + .rst_ni, + .req_valid_i ( req_i.ar_valid ), + .req_valid_o ( throttled_ar_valid ), + .req_ready_i ( rsp_i.ar_ready ), + .req_ready_o ( throttled_ar_ready ), + .rsp_valid_i ( rsp_i.r_valid & rsp_i.r.last ), + .rsp_ready_i ( req_i.r_ready ), + .credit_i ( r_credit_i ) + ); + + // connect the throttled request bus (its a through connection - except for the ax valids) + always_comb begin : gen_throttled_req_conn + req_o = req_i; + req_o.aw_valid = throttled_aw_valid; + req_o.ar_valid = throttled_ar_valid; + end + + // connect the throttled response bus (its a through connection - except for the ax readies) + always_comb begin : gen_throttled_rsp_conn + rsp_o = rsp_i; + rsp_o.aw_ready = throttled_aw_ready; + rsp_o.ar_ready = throttled_ar_ready; + end + +endmodule : axi_throttle diff --git a/hw/vendor/pulp_platform_axi/src/axi_to_axi_lite.sv b/hw/vendor/pulp_platform_axi/src/axi_to_axi_lite.sv index c75887a61..ad2e41414 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_to_axi_lite.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_to_axi_lite.sv @@ -13,7 +13,6 @@ // - Wolfgang Roenninger // - Andreas Kurth // - Fabian Schuiki -// - Florian Zaruba /// An AXI4+ATOP to AXI4-Lite converter with atomic transaction and burst support. module axi_to_axi_lite #( @@ -23,6 +22,7 @@ module axi_to_axi_lite #( parameter int unsigned AxiUserWidth = 32'd0, parameter int unsigned AxiMaxWriteTxns = 32'd0, parameter int unsigned AxiMaxReadTxns = 32'd0, + parameter bit FullBW = 0, // ID Queue in Full BW mode in axi_burst_splitter parameter bit FallThrough = 1'b1, // FIFOs in Fall through mode in ID reflect parameter type full_req_t = logic, parameter type full_resp_t = logic, @@ -47,8 +47,8 @@ module axi_to_axi_lite #( axi_atop_filter #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( AxiMaxWriteTxns ), - .req_t ( full_req_t ), - .resp_t ( full_resp_t ) + .axi_req_t ( full_req_t ), + .axi_resp_t ( full_resp_t ) ) i_axi_atop_filter( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -62,12 +62,13 @@ module axi_to_axi_lite #( axi_burst_splitter #( .MaxReadTxns ( AxiMaxReadTxns ), .MaxWriteTxns ( AxiMaxWriteTxns ), + .FullBW ( FullBW ), .AddrWidth ( AxiAddrWidth ), .DataWidth ( AxiDataWidth ), .IdWidth ( AxiIdWidth ), .UserWidth ( AxiUserWidth ), - .req_t ( full_req_t ), - .resp_t ( full_resp_t ) + .axi_req_t ( full_req_t ), + .axi_resp_t ( full_resp_t ) ) i_axi_burst_splitter ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -256,7 +257,8 @@ module axi_to_axi_lite_intf #( parameter int unsigned AXI_MAX_WRITE_TXNS = 32'd1, /// Maximum number of outstanding reads. parameter int unsigned AXI_MAX_READ_TXNS = 32'd1, - parameter bit FALL_THROUGH = 1'b1 + parameter bit FALL_THROUGH = 1'b1, + parameter bit FULL_BW = 0 ) ( input logic clk_i, input logic rst_ni, @@ -305,6 +307,7 @@ module axi_to_axi_lite_intf #( .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ), .AxiMaxReadTxns ( AXI_MAX_READ_TXNS ), .FallThrough ( FALL_THROUGH ), // FIFOs in Fall through mode in ID reflect + .FullBW ( FULL_BW ), .full_req_t ( full_req_t ), .full_resp_t ( full_resp_t ), .lite_req_t ( lite_req_t ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_to_detailed_mem.sv b/hw/vendor/pulp_platform_axi/src/axi_to_detailed_mem.sv new file mode 100644 index 000000000..2ea01f3bf --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_to_detailed_mem.sv @@ -0,0 +1,746 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Michael Rogenmoser +// - Thomas Benz + +`include "common_cells/registers.svh" +/// AXI4+ATOP slave module which translates AXI bursts into a memory stream. +/// If both read and write channels of the AXI4+ATOP are active, both will have an +/// utilization of 50%. +module axi_to_detailed_mem #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 1, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 1, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 1, + /// AXI4+ATOP user width. + parameter int unsigned UserWidth = 1, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 1, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// Dependent parameter, do not override. Memory address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + localparam type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + localparam type mem_strb_t = logic [DataWidth/NumBanks/8-1:0], + /// Dependent parameter, do not override. Memory id type. + localparam type mem_id_t = logic [IdWidth-1:0], + /// Dependent parameter, do not override. Memory user type. + localparam type mem_user_t = logic [UserWidth-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, lock signal. + output logic [NumBanks-1:0] mem_lock_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, ID. Response ID is managed internally, ensure in-order responses. + output mem_id_t [NumBanks-1:0] mem_id_o, + /// Memory stream master, user signal. Ax channel user bits used. + output mem_user_t [NumBanks-1:0] mem_user_o, + /// Memory stream master, cache signal. + output axi_pkg::cache_t [NumBanks-1:0] mem_cache_o, + /// Memory stream master, protection signal. + output axi_pkg::prot_t [NumBanks-1:0] mem_prot_o, + /// Memory stream master, QOS signal. + output axi_pkg::qos_t [NumBanks-1:0] mem_qos_o, + /// Memory stream master, region signal. + output axi_pkg::region_t [NumBanks-1:0] mem_region_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i, + /// Memory stream master, error response. + input logic [NumBanks-1:0] mem_err_i, + /// Memory stream master, read response exclusive access OK. + input logic [NumBanks-1:0] mem_exokay_i +); + + typedef logic [DataWidth-1:0] axi_data_t; + typedef logic [DataWidth/8-1:0] axi_strb_t; + typedef logic [IdWidth-1:0] axi_id_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_strb_t strb; + axi_data_t wdata; + logic we; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; + } mem_req_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_strb_t strb; + axi_id_t id; + logic last; + axi_pkg::qos_t qos; + axi_pkg::size_t size; + logic write; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::region_t region; + } meta_t; + + typedef struct packed { + axi_data_t data; + logic [NumBanks-1:0] err; + logic [NumBanks-1:0] exokay; + } mem_rsp_t; + + mem_rsp_t mem_rdata, + m2s_resp; + axi_pkg::len_t r_cnt_d, r_cnt_q, + w_cnt_d, w_cnt_q; + logic arb_valid, arb_ready, + rd_valid, rd_ready, + wr_valid, wr_ready, + sel_b, sel_buf_b, + sel_r, sel_buf_r, + sel_valid, sel_ready, + sel_buf_valid, sel_buf_ready, + sel_lock_d, sel_lock_q, + meta_valid, meta_ready, + meta_buf_valid, meta_buf_ready, + meta_sel_d, meta_sel_q, + m2s_req_valid, m2s_req_ready, + m2s_resp_valid, m2s_resp_ready, + mem_req_valid, mem_req_ready, + mem_rvalid; + mem_req_t m2s_req, + mem_req; + meta_t rd_meta, + rd_meta_d, rd_meta_q, + wr_meta, + wr_meta_d, wr_meta_q, + meta, meta_buf; + + assign busy_o = axi_req_i.aw_valid | axi_req_i.ar_valid | axi_req_i.w_valid | + axi_resp_o.b_valid | axi_resp_o.r_valid | + (r_cnt_q > 0) | (w_cnt_q > 0); + + // Handle reads. + always_comb begin + // Default assignments + axi_resp_o.ar_ready = 1'b0; + rd_meta_d = rd_meta_q; + rd_meta = meta_t'{default: '0}; + rd_valid = 1'b0; + r_cnt_d = r_cnt_q; + // Handle R burst in progress. + if (r_cnt_q > '0) begin + rd_meta_d.last = (r_cnt_q == 8'd1); + rd_meta = rd_meta_d; + rd_meta.addr = rd_meta_q.addr + axi_pkg::num_bytes(rd_meta_q.size); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d--; + rd_meta_d.addr = rd_meta.addr; + end + // Handle new AR if there is one. + end else if (axi_req_i.ar_valid) begin + rd_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), + atop: '0, + lock: axi_req_i.ar.lock, + strb: '0, + id: axi_req_i.ar.id, + last: (axi_req_i.ar.len == '0), + qos: axi_req_i.ar.qos, + size: axi_req_i.ar.size, + write: 1'b0, + user: axi_req_i.ar.user, + cache: axi_req_i.ar.cache, + prot: axi_req_i.ar.prot, + region: axi_req_i.ar.region + }; + rd_meta = rd_meta_d; + rd_meta.addr = addr_t'(axi_req_i.ar.addr); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d = axi_req_i.ar.len; + axi_resp_o.ar_ready = 1'b1; + end + end + end + + // Handle writes. + always_comb begin + // Default assignments + axi_resp_o.aw_ready = 1'b0; + axi_resp_o.w_ready = 1'b0; + wr_meta_d = wr_meta_q; + wr_meta = meta_t'{default: '0}; + wr_valid = 1'b0; + w_cnt_d = w_cnt_q; + // Handle W bursts in progress. + if (w_cnt_q > '0) begin + wr_meta_d.last = (w_cnt_q == 8'd1); + wr_meta = wr_meta_d; + wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); + if (axi_req_i.w_valid) begin + wr_valid = 1'b1; + wr_meta.strb = axi_req_i.w.strb; + if (wr_ready) begin + axi_resp_o.w_ready = 1'b1; + w_cnt_d--; + wr_meta_d.addr = wr_meta.addr; + end + end + // Handle new AW if there is one. + end else if (axi_req_i.aw_valid && axi_req_i.w_valid) begin + wr_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), + atop: axi_req_i.aw.atop, + lock: axi_req_i.aw.lock, + strb: axi_req_i.w.strb, + id: axi_req_i.aw.id, + last: (axi_req_i.aw.len == '0), + qos: axi_req_i.aw.qos, + size: axi_req_i.aw.size, + write: 1'b1, + user: axi_req_i.aw.user, + cache: axi_req_i.aw.cache, + prot: axi_req_i.aw.prot, + region: axi_req_i.aw.region + }; + wr_meta = wr_meta_d; + wr_meta.addr = addr_t'(axi_req_i.aw.addr); + wr_valid = 1'b1; + if (wr_ready) begin + w_cnt_d = axi_req_i.aw.len; + axi_resp_o.aw_ready = 1'b1; + axi_resp_o.w_ready = 1'b1; + end + end + end + + // Arbitrate between reads and writes. + stream_mux #( + .DATA_T ( meta_t ), + .N_INP ( 32'd2 ) + ) i_ax_mux ( + .inp_data_i ({wr_meta, rd_meta }), + .inp_valid_i ({wr_valid, rd_valid}), + .inp_ready_o ({wr_ready, rd_ready}), + .inp_sel_i ( meta_sel_d ), + .oup_data_o ( meta ), + .oup_valid_o ( arb_valid ), + .oup_ready_i ( arb_ready ) + ); + always_comb begin + meta_sel_d = meta_sel_q; + sel_lock_d = sel_lock_q; + if (sel_lock_q) begin + meta_sel_d = meta_sel_q; + if (arb_valid && arb_ready) begin + sel_lock_d = 1'b0; + end + end else begin + if (wr_valid ^ rd_valid) begin + // If either write or read is valid but not both, select the valid one. + meta_sel_d = wr_valid; + end else if (wr_valid && rd_valid) begin + // If both write and read are valid, decide according to QoS then burst properties. + // Prioritize higher QoS. + if (wr_meta.qos > rd_meta.qos) begin + meta_sel_d = 1'b1; + end else if (rd_meta.qos > wr_meta.qos) begin + meta_sel_d = 1'b0; + // Decide requests with identical QoS. + end else if (wr_meta.qos == rd_meta.qos) begin + // 1. Prioritize individual writes over read bursts. + // Rationale: Read bursts can be interleaved on AXI but write bursts cannot. + if (wr_meta.last && !rd_meta.last) begin + meta_sel_d = 1'b1; + // 2. Prioritize ongoing burst. + // Rationale: Stalled bursts create back-pressure or require costly buffers. + end else if (w_cnt_q > '0) begin + meta_sel_d = 1'b1; + end else if (r_cnt_q > '0) begin + meta_sel_d = 1'b0; + // 3. Otherwise arbitrate round robin to prevent starvation. + end else begin + meta_sel_d = ~meta_sel_q; + end + end + end + // Lock arbitration if valid but not yet ready. + if (arb_valid && !arb_ready) begin + sel_lock_d = 1'b1; + end + end + end + + // Fork arbitrated stream to meta data, memory requests, and R/B channel selection. + stream_fork #( + .N_OUP ( 32'd3 ) + ) i_fork ( + .clk_i, + .rst_ni, + .valid_i ( arb_valid ), + .ready_o ( arb_ready ), + .valid_o ({sel_valid, meta_valid, m2s_req_valid}), + .ready_i ({sel_ready, meta_ready, m2s_req_ready}) + ); + + assign sel_b = meta.write & meta.last; + assign sel_r = ~meta.write | meta.atop[5]; + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( logic[1:0] ) + ) i_sel_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ({sel_b, sel_r }), + .valid_i ( sel_valid ), + .ready_o ( sel_ready ), + .data_o ({sel_buf_b, sel_buf_r}), + .valid_o ( sel_buf_valid ), + .ready_i ( sel_buf_ready ), + .usage_o ( /* unused */ ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( meta_t ) + ) i_meta_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ( meta ), + .valid_i ( meta_valid ), + .ready_o ( meta_ready ), + .data_o ( meta_buf ), + .valid_o ( meta_buf_valid ), + .ready_i ( meta_buf_ready ), + .usage_o ( /* unused */ ) + ); + + // Assemble the actual memory request from meta information and write data. + assign m2s_req = mem_req_t'{ + addr: meta.addr, + atop: meta.atop, + lock: meta.lock, + strb: axi_req_i.w.strb, + wdata: axi_req_i.w.data, + we: meta.write, + id: meta.id, + user: meta.user, + cache: meta.cache, + prot: meta.prot, + qos: meta.qos, + region: meta.region + }; + + // Interface memory as stream. + stream_to_mem #( + .mem_req_t ( mem_req_t ), + .mem_resp_t ( mem_rsp_t ), + .BufDepth ( BufDepth ) + ) i_stream_to_mem ( + .clk_i, + .rst_ni, + .req_i ( m2s_req ), + .req_valid_i ( m2s_req_valid ), + .req_ready_o ( m2s_req_ready ), + .resp_o ( m2s_resp ), + .resp_valid_o ( m2s_resp_valid ), + .resp_ready_i ( m2s_resp_ready ), + .mem_req_o ( mem_req ), + .mem_req_valid_o ( mem_req_valid ), + .mem_req_ready_i ( mem_req_ready ), + .mem_resp_i ( mem_rdata ), + .mem_resp_valid_i ( mem_rvalid ) + ); + + typedef struct packed { + axi_pkg::atop_t atop; + logic lock; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; + } tmp_atop_t; + + tmp_atop_t mem_req_atop; + tmp_atop_t [NumBanks-1:0] banked_req_atop; + + assign mem_req_atop = '{ + atop: mem_req.atop, + lock: mem_req.lock, + id: mem_req.id, + user: mem_req.user, + cache: mem_req.cache, + prot: mem_req.prot, + qos: mem_req.qos, + region: mem_req.region + }; + + for (genvar i = 0; i < NumBanks; i++) begin + assign mem_atop_o [i] = banked_req_atop[i].atop; + assign mem_lock_o [i] = banked_req_atop[i].lock; + assign mem_id_o [i] = banked_req_atop[i].id; + assign mem_user_o [i] = banked_req_atop[i].user; + assign mem_cache_o [i] = banked_req_atop[i].cache; + assign mem_prot_o [i] = banked_req_atop[i].prot; + assign mem_qos_o [i] = banked_req_atop[i].qos; + assign mem_region_o[i] = banked_req_atop[i].region; + end + + logic [NumBanks-1:0][1:0] tmp_ersp; + logic [NumBanks-1:0][1:0] bank_ersp; + for (genvar i = 0; i < NumBanks; i++) begin + assign mem_rdata.err[i] = tmp_ersp[i][0]; + assign mem_rdata.exokay[i] = tmp_ersp[i][1]; + assign bank_ersp[i][0] = mem_err_i[i]; + assign bank_ersp[i][1] = mem_exokay_i[i]; + end + + // Split single memory request to desired number of banks. + mem_to_banks_detailed #( + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .RUserWidth ( 2 ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ), + .WUserWidth ( $bits(tmp_atop_t) ) + ) i_mem_to_banks ( + .clk_i, + .rst_ni, + .req_i ( mem_req_valid ), + .gnt_o ( mem_req_ready ), + .addr_i ( mem_req.addr ), + .wdata_i ( mem_req.wdata ), + .strb_i ( mem_req.strb ), + .wuser_i ( mem_req_atop ), + .we_i ( mem_req.we ), + .rvalid_o ( mem_rvalid ), + .rdata_o ( mem_rdata.data ), + .ruser_o ( tmp_ersp ), + .bank_req_o ( mem_req_o ), + .bank_gnt_i ( mem_gnt_i ), + .bank_addr_o ( mem_addr_o ), + .bank_wdata_o ( mem_wdata_o ), + .bank_strb_o ( mem_strb_o ), + .bank_wuser_o ( banked_req_atop ), + .bank_we_o ( mem_we_o ), + .bank_rvalid_i ( mem_rvalid_i ), + .bank_rdata_i ( mem_rdata_i ), + .bank_ruser_i ( bank_ersp ) + ); + + // Join memory read data and meta data stream. + logic mem_join_valid, mem_join_ready; + stream_join #( + .N_INP ( 32'd2 ) + ) i_join ( + .inp_valid_i ({m2s_resp_valid, meta_buf_valid}), + .inp_ready_o ({m2s_resp_ready, meta_buf_ready}), + .oup_valid_o ( mem_join_valid ), + .oup_ready_i ( mem_join_ready ) + ); + + // Dynamically fork the joined stream to B and R channels. + stream_fork_dynamic #( + .N_OUP ( 32'd2 ) + ) i_fork_dynamic ( + .clk_i, + .rst_ni, + .valid_i ( mem_join_valid ), + .ready_o ( mem_join_ready ), + .sel_i ({sel_buf_b, sel_buf_r }), + .sel_valid_i ( sel_buf_valid ), + .sel_ready_o ( sel_buf_ready ), + .valid_o ({axi_resp_o.b_valid, axi_resp_o.r_valid}), + .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) + ); + + localparam NumBytesPerBank = DataWidth/NumBanks/8; + + logic [NumBanks-1:0] meta_buf_bank_strb, meta_buf_size_enable; + logic resp_b_err, resp_b_exokay, resp_r_err, resp_r_exokay; + + // Collect `err` and `exokay` from all banks + // To ensure correct propagation, `err` is grouped with `OR` and `exokay` is grouped with `AND`. + for (genvar i = 0; i < NumBanks; i++) begin + // Set active write banks based on strobe + assign meta_buf_bank_strb[i] = |meta_buf.strb[i*NumBytesPerBank +: NumBytesPerBank]; + // Set active read banks based on size and address offset: (bank.end > addr) && (bank.start < addr+size) + assign meta_buf_size_enable[i] = ((i*NumBytesPerBank + NumBytesPerBank) > (meta_buf.addr % DataWidth/8)) && + ((i*NumBytesPerBank) < ((meta_buf.addr % DataWidth/8) + 1< $stable(axi_req_i.ar)) + else $error("AR must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.r_valid && !axi_req_i.r_ready |=> $stable(axi_resp_o.r)) + else $error("R must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.aw_valid && !axi_resp_o.aw_ready |=> $stable(axi_req_i.aw)) + else $error("AW must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.w_valid && !axi_resp_o.w_ready |=> $stable(axi_req_i.w)) + else $error("W must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.b_valid && !axi_req_i.b_ready |=> $stable(axi_resp_o.b)) + else $error("B must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) axi_req_i.ar_valid && axi_req_i.ar.len > 0 |-> + axi_req_i.ar.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) axi_req_i.aw_valid && axi_req_i.aw.len > 0 |-> + axi_req_i.aw.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) meta_valid && meta.atop != '0 |-> meta.write) + else $warning("Unexpected atomic operation on read."); + `endif + // pragma translate_on +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" +/// Interface wrapper for module `axi_to_mem`. +module axi_to_detailed_mem_intf #( + /// See `axi_to_mem`, parameter `AddrWidth`. + parameter int unsigned ADDR_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `DataWidth`. + parameter int unsigned DATA_WIDTH = 32'd0, + /// AXI4+ATOP ID width. + parameter int unsigned ID_WIDTH = 32'd0, + /// AXI4+ATOP user width. + parameter int unsigned USER_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `NumBanks`. + parameter int unsigned NUM_BANKS = 32'd0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + localparam type addr_t = logic [ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + localparam type mem_data_t = logic [DATA_WIDTH/NUM_BANKS-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + localparam type mem_strb_t = logic [DATA_WIDTH/NUM_BANKS/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave slv, + /// See `axi_to_mem`, port `mem_req_o`. + output logic [NUM_BANKS-1:0] mem_req_o, + /// See `axi_to_mem`, port `mem_gnt_i`. + input logic [NUM_BANKS-1:0] mem_gnt_i, + /// See `axi_to_mem`, port `mem_addr_o`. + output addr_t [NUM_BANKS-1:0] mem_addr_o, + /// See `axi_to_mem`, port `mem_wdata_o`. + output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, + /// See `axi_to_mem`, port `mem_strb_o`. + output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, + /// See `axi_to_mem`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, + /// See `axi_to_mem`, port `mem_lock_o`. + output logic [NUM_BANKS-1:0] mem_lock_o, + /// See `axi_to_mem`, port `mem_we_o`. + output logic [NUM_BANKS-1:0] mem_we_o, + /// See `axi_to_mem`, port `mem_id_o`. + output logic [NUM_BANKS-1:0] mem_id_o, + /// See `axi_to_mem`, port `mem_user_o`. + output logic [NUM_BANKS-1:0] mem_user_o, + /// See `axi_to_mem`, port `mem_cache_o`. + output axi_pkg::cache_t [NUM_BANKS-1:0] mem_cache_o, + /// See `axi_to_mem`, port `mem_prot_o`. + output axi_pkg::prot_t [NUM_BANKS-1:0] mem_prot_o, + /// See `axi_to_mem`, port `mem_qos_o`. + output axi_pkg::qos_t [NUM_BANKS-1:0] mem_qos_o, + /// See `axi_to_mem`, port `mem_region_o`. + output axi_pkg::region_t [NUM_BANKS-1:0] mem_region_o, + /// See `axi_to_mem`, port `mem_rvalid_i`. + input logic [NUM_BANKS-1:0] mem_rvalid_i, + /// See `axi_to_mem`, port `mem_rdata_i`. + input mem_data_t [NUM_BANKS-1:0] mem_rdata_i, + /// See `axi_to_mem`, port `mem_err_i`. + input logic [NUM_BANKS-1:0] mem_err_i, + /// See `axi_to_mem`, port `mem_exokay_i`. + input logic [NUM_BANKS-1:0] mem_exokay_i +); + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + req_t req; + resp_t resp; + `AXI_ASSIGN_TO_REQ(req, slv) + `AXI_ASSIGN_FROM_RESP(slv, resp) + axi_to_detailed_mem #( + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .UserWidth ( USER_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_detailed_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( req ), + .axi_resp_o ( resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_lock_o, + .mem_we_o, + .mem_id_o, + .mem_user_o, + .mem_cache_o, + .mem_prot_o, + .mem_qos_o, + .mem_region_o, + .mem_rvalid_i, + .mem_rdata_i, + .mem_err_i, + .mem_exokay_i + ); +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_to_mem.sv b/hw/vendor/pulp_platform_axi/src/axi_to_mem.sv new file mode 100644 index 000000000..37992d449 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_to_mem.sv @@ -0,0 +1,213 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Michael Rogenmoser +// - Thomas Benz + +`include "common_cells/registers.svh" +/// AXI4+ATOP slave module which translates AXI bursts into a memory stream. +/// If both read and write channels of the AXI4+ATOP are active, both will have an +/// utilization of 50%. +module axi_to_mem #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// Dependent parameter, do not override. Memory address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + localparam type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + localparam type mem_strb_t = logic [DataWidth/NumBanks/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i +); + + axi_to_detailed_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .UserWidth ( 1 ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_detailed_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( axi_req_i ), + .axi_resp_o ( axi_resp_o ), + .mem_req_o ( mem_req_o ), + .mem_gnt_i ( mem_gnt_i ), + .mem_addr_o ( mem_addr_o ), + .mem_wdata_o ( mem_wdata_o ), + .mem_strb_o ( mem_strb_o ), + .mem_atop_o ( mem_atop_o ), + .mem_lock_o (), + .mem_we_o ( mem_we_o ), + .mem_id_o (), + .mem_user_o (), + .mem_cache_o (), + .mem_prot_o (), + .mem_qos_o (), + .mem_region_o (), + .mem_rvalid_i ( mem_rvalid_i ), + .mem_rdata_i ( mem_rdata_i ), + .mem_err_i ('0), + .mem_exokay_i ('0) + ); + +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" +/// Interface wrapper for module `axi_to_mem`. +module axi_to_mem_intf #( + /// See `axi_to_mem`, parameter `AddrWidth`. + parameter int unsigned ADDR_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `DataWidth`. + parameter int unsigned DATA_WIDTH = 32'd0, + /// AXI4+ATOP ID width. + parameter int unsigned ID_WIDTH = 32'd0, + /// AXI4+ATOP user width. + parameter int unsigned USER_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `NumBanks`. + parameter int unsigned NUM_BANKS = 32'd0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + localparam type addr_t = logic [ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + localparam type mem_data_t = logic [DATA_WIDTH/NUM_BANKS-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + localparam type mem_strb_t = logic [DATA_WIDTH/NUM_BANKS/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave slv, + /// See `axi_to_mem`, port `mem_req_o`. + output logic [NUM_BANKS-1:0] mem_req_o, + /// See `axi_to_mem`, port `mem_gnt_i`. + input logic [NUM_BANKS-1:0] mem_gnt_i, + /// See `axi_to_mem`, port `mem_addr_o`. + output addr_t [NUM_BANKS-1:0] mem_addr_o, + /// See `axi_to_mem`, port `mem_wdata_o`. + output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, + /// See `axi_to_mem`, port `mem_strb_o`. + output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, + /// See `axi_to_mem`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, + /// See `axi_to_mem`, port `mem_we_o`. + output logic [NUM_BANKS-1:0] mem_we_o, + /// See `axi_to_mem`, port `mem_rvalid_i`. + input logic [NUM_BANKS-1:0] mem_rvalid_i, + /// See `axi_to_mem`, port `mem_rdata_i`. + input mem_data_t [NUM_BANKS-1:0] mem_rdata_i +); + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + req_t req; + resp_t resp; + `AXI_ASSIGN_TO_REQ(req, slv) + `AXI_ASSIGN_FROM_RESP(slv, resp) + axi_to_mem #( + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( req ), + .axi_resp_o ( resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_to_mem_banked.sv b/hw/vendor/pulp_platform_axi/src/axi_to_mem_banked.sv new file mode 100644 index 000000000..548339f37 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_to_mem_banked.sv @@ -0,0 +1,435 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Wolfgang Rönninger +// - Michael Rogenmoser + +/// AXI4+ATOP to banked SRAM memory slave. Allows for parallel read and write transactions. +/// Has higher throughput than `axi_to_mem`, however needs more hardware. +/// +/// The used address space starts at 0x0 and ends at the capacity of all memory banks combined. +/// The higher address bits are ignored for accesses. +module axi_to_mem_banked #( + /// AXI4+ATOP ID width + parameter int unsigned AxiIdWidth = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AxiAddrWidth = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AxiDataWidth = 32'd0, + /// AXI4+ATOP AW channel struct + parameter type axi_aw_chan_t = logic, + /// AXI4+ATOP W channel struct + parameter type axi_w_chan_t = logic, + /// AXI4+ATOP B channel struct + parameter type axi_b_chan_t = logic, + /// AXI4+ATOP AR channel struct + parameter type axi_ar_chan_t = logic, + /// AXI4+ATOP R channel struct + parameter type axi_r_chan_t = logic, + /// AXI4+ATOP request struct + parameter type axi_req_t = logic, + /// AXI4+ATOP response struct + parameter type axi_resp_t = logic, + /// Number of memory banks / macros + /// Has to satisfy: + /// - MemNumBanks >= 2 * AxiDataWidth / MemDataWidth + /// - MemNumBanks is a power of 2. + parameter int unsigned MemNumBanks = 32'd4, + /// Address width of an individual memory bank. This is treated as a word address. + parameter int unsigned MemAddrWidth = 32'd11, + /// Data width of the memory macros. + /// Has to satisfy: + /// - AxiDataWidth % MemDataWidth = 0 + parameter int unsigned MemDataWidth = 32'd32, + /// Read latency of the connected memory in cycles + parameter int unsigned MemLatency = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Address type of the memory request. + parameter type mem_addr_t = logic [MemAddrWidth-1:0], + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Atomic operation type for the memory request. + parameter type mem_atop_t = axi_pkg::atop_t, + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Data type for the memory request. + parameter type mem_data_t = logic [MemDataWidth-1:0], + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Byte strobe/enable signal for the memory request. + parameter type mem_strb_t = logic [MemDataWidth/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// AXI4+ATOP slave port, request struct + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response struct + output axi_resp_t axi_resp_o, + /// Memory bank request + output logic [MemNumBanks-1:0] mem_req_o, + /// Memory request grant + input logic [MemNumBanks-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MemNumBanks-1:0] mem_add_o, + /// Write request enable, active high + output logic [MemNumBanks-1:0] mem_we_o, + /// Write data + output mem_data_t [MemNumBanks-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MemNumBanks-1:0] mem_be_o, + /// Atomic operation + output mem_atop_t [MemNumBanks-1:0] mem_atop_o, + /// Read data response + input mem_data_t [MemNumBanks-1:0] mem_rdata_i, + /// Status output, busy flag of `axi_to_mem` + output logic [1:0] axi_to_mem_busy_o +); + /// This specifies the number of banks needed to have the full data bandwidth of one + /// AXI data channel. + localparam int unsigned BanksPerAxiChannel = AxiDataWidth / MemDataWidth; + /// Offset of the byte address from AXI to determine, where the selection signal for the + /// memory bank should start. + localparam int unsigned BankSelOffset = $clog2(MemDataWidth / 32'd8); + /// Selection signal width of the xbar. This is the reason for power of two banks, otherwise + /// There are holes in the address mapping. + localparam int unsigned BankSelWidth = cf_math_pkg::idx_width(MemNumBanks); + typedef logic [BankSelWidth-1:0] xbar_sel_t; + + // Typedef for defining the channels + typedef enum logic { + ReadAccess = 1'b0, + WriteAccess = 1'b1 + } access_type_e; + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + + /// Payload definition which is sent over the xbar between the macros and the read/write unit. + typedef struct packed { + /// Address for the memory access + mem_addr_t addr; + /// Write enable, active high + logic we; + /// Write data + mem_data_t wdata; + /// Strobe signal, byte enable + mem_strb_t wstrb; + /// Atomic operation, from AXI + mem_atop_t atop; + } xbar_payload_t; + + /// Read data definition for the shift register, which samples the read response data + typedef struct packed { + /// Selection signal for response routing + xbar_sel_t sel; + /// Selection is valid + logic valid; + } read_sel_t; + + axi_req_t [1:0] mem_axi_reqs; + axi_resp_t [1:0] mem_axi_resps; + + // Fixed select `axi_demux` to split reads and writes to the two `axi_to_mem` + axi_demux #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport ( 1'b1 ), + .aw_chan_t ( axi_aw_chan_t ), + .w_chan_t ( axi_w_chan_t ), + .b_chan_t ( axi_b_chan_t ), + .ar_chan_t ( axi_ar_chan_t ), + .r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 32'd2 ), + .MaxTrans ( MemLatency+2 ), // allow multiple Ax vectors to not starve W channel + .AxiLookBits ( 32'd1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ), // Can be set as ports are statically selected -> reduces HW + .SpillAw ( 1'b1 ), + .SpillW ( 1'b1 ), + .SpillB ( 1'b1 ), + .SpillAr ( 1'b1 ), + .SpillR ( 1'b1 ) + ) i_axi_demux ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_aw_select_i ( WriteAccess ), + .slv_ar_select_i ( ReadAccess ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( mem_axi_reqs ), + .mst_resps_i ( mem_axi_resps ) + ); + + xbar_payload_t [1:0][BanksPerAxiChannel-1:0] inter_payload; + xbar_sel_t [1:0][BanksPerAxiChannel-1:0] inter_sel; + logic [1:0][BanksPerAxiChannel-1:0] inter_valid, inter_ready; + + // axi_to_mem protocol converter + for (genvar i = 0; i < 2; i++) begin : gen_axi_to_mem + axi_addr_t [BanksPerAxiChannel-1:0] req_addr; // This is a byte address + mem_data_t [BanksPerAxiChannel-1:0] req_wdata, res_rdata; + mem_strb_t [BanksPerAxiChannel-1:0] req_wstrb; + mem_atop_t [BanksPerAxiChannel-1:0] req_atop; + + logic [BanksPerAxiChannel-1:0] req_we, res_valid; + + // Careful, request / grant + // Only assert grant, if there is a ready + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( AxiIdWidth ), + .NumBanks ( BanksPerAxiChannel ), + .BufDepth ( MemLatency ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem ( + .clk_i, + .rst_ni, + .busy_o ( axi_to_mem_busy_o[i] ), + .axi_req_i ( mem_axi_reqs[i] ), + .axi_resp_o ( mem_axi_resps[i] ), + .mem_req_o ( inter_valid[i] ), + .mem_gnt_i ( inter_ready[i] & inter_valid[i] ), // convert valid/ready to req/gnt + .mem_addr_o ( req_addr ), + .mem_wdata_o ( req_wdata ), + .mem_strb_o ( req_wstrb ), + .mem_atop_o ( req_atop ), + .mem_we_o ( req_we ), + .mem_rvalid_i ( res_valid ), + .mem_rdata_i ( res_rdata ) + ); + // Pack the payload data together + for (genvar j = 0; unsigned'(j) < BanksPerAxiChannel; j++) begin : gen_response_mux + // Cut out the bank selection signal. + assign inter_sel[i][j] = req_addr[j][BankSelOffset+:BankSelWidth]; + + // Assign the xbar payload. + assign inter_payload[i][j] = xbar_payload_t'{ + // Cut out the word address for the banks. + addr: req_addr[j][(BankSelOffset+BankSelWidth)+:MemAddrWidth], + we: req_we[j], + wdata: req_wdata[j], + wstrb: req_wstrb[j], + atop: req_atop[j], + default: '0 + }; + + // Cut out the portion of the address for the bank selection, each bank is word addressed! + read_sel_t r_shift_inp, r_shift_oup; + // Pack the selection into the shift register + assign r_shift_inp = read_sel_t'{ + sel: inter_sel[i][j], // Selection for response multiplexer + valid: inter_valid[i][j] & inter_ready[i][j], // Valid when req to SRAM + default: '0 + }; + + // Select the right read response data. + // Writes should also generate a `response`. + assign res_valid[j] = r_shift_oup.valid; + assign res_rdata[j] = mem_rdata_i[r_shift_oup.sel]; + + // Connect for the response data `MemLatency` cycles after a request was made to the xbar. + shift_reg #( + .dtype ( read_sel_t ), + .Depth ( MemLatency ) + ) i_shift_reg_rdata_mux ( + .clk_i, + .rst_ni, + .d_i ( r_shift_inp ), + .d_o ( r_shift_oup ) + ); + end + end + + // Xbar to arbitrate data over the different memory banks + xbar_payload_t [MemNumBanks-1:0] mem_payload; + + stream_xbar #( + .NumInp ( 32'd2 * BanksPerAxiChannel ), + .NumOut ( MemNumBanks ), + .payload_t ( xbar_payload_t ), + .OutSpillReg ( 1'b0 ), + .ExtPrio ( 1'b0 ), + .AxiVldRdy ( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_stream_xbar ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .data_i ( inter_payload ), + .sel_i ( inter_sel ), + .valid_i ( inter_valid ), + .ready_o ( inter_ready ), + .data_o ( mem_payload ), + .idx_o ( /*not used*/ ), + .valid_o ( mem_req_o ), + .ready_i ( mem_gnt_i ) + ); + + // Memory request output assignment + for (genvar i = 0; unsigned'(i) < MemNumBanks; i++) begin : gen_mem_outp + assign mem_add_o[i] = mem_payload[i].addr; + assign mem_we_o[i] = mem_payload[i].we; + assign mem_wdata_o[i] = mem_payload[i].wdata; + assign mem_be_o[i] = mem_payload[i].wstrb; + assign mem_atop_o[i] = mem_payload[i].atop; + end + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AxiIdWidth >= 32'd1) else $fatal(1, "AxiIdWidth must be at least 1!"); + assert (AxiAddrWidth >= 32'd1) else $fatal(1, "AxiAddrWidth must be at least 1!"); + assert (AxiDataWidth >= 32'd1) else $fatal(1, "AxiDataWidth must be at least 1!"); + assert (MemNumBanks >= 32'd2 * AxiDataWidth / MemDataWidth) else + $fatal(1, "MemNumBanks has to be >= 2 * AxiDataWidth / MemDataWidth"); + assert (MemLatency >= 32'd1) else $fatal(1, "MemLatency has to be at least 1!"); + assert ($onehot(MemNumBanks)) else $fatal(1, "MemNumBanks has to be a power of 2."); + assert (MemAddrWidth >= 32'd1) else $fatal(1, "MemAddrWidth must be at least 1!"); + assert (MemDataWidth >= 32'd1) else $fatal(1, "MemDataWidth must be at least 1!"); + assert (AxiDataWidth % MemDataWidth == 0) else + $fatal(1, "MemDataWidth has to be a divisor of AxiDataWidth."); + end +`endif +// pragma translate_on +endmodule + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem` +module axi_to_mem_banked_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'd0, + /// Number of memory banks / macros + /// Has to satisfy: + /// - MemNumBanks >= 2 * AxiDataWidth / MemDataWidth + /// - MemNumBanks is a power of 2. + parameter int unsigned MEM_NUM_BANKS = 32'd4, + /// Address width of an individual memory bank. + parameter int unsigned MEM_ADDR_WIDTH = 32'd11, + /// Data width of the memory macros. + /// Has to satisfy: + /// - AxiDataWidth % MemDataWidth = 0 + parameter int unsigned MEM_DATA_WIDTH = 32'd32, + /// Read latency of the connected memory in cycles + parameter int unsigned MEM_LATENCY = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + // DEPENDENT PARAMETERS, DO NOT OVERWRITE! + parameter type mem_addr_t = logic [MEM_ADDR_WIDTH-1:0], + parameter type mem_atop_t = logic [5:0], + parameter type mem_data_t = logic [MEM_DATA_WIDTH-1:0], + parameter type mem_strb_t = logic [MEM_DATA_WIDTH/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// AXI4+ATOP slave port + AXI_BUS.Slave slv, + /// Memory bank request + output logic [MEM_NUM_BANKS-1:0] mem_req_o, + /// Memory request grant + input logic [MEM_NUM_BANKS-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MEM_NUM_BANKS-1:0] mem_add_o, + /// Write request enable, active high + output logic [MEM_NUM_BANKS-1:0] mem_we_o, + /// Write data + output mem_data_t [MEM_NUM_BANKS-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MEM_NUM_BANKS-1:0] mem_be_o, + /// Atomic operation + output mem_atop_t [MEM_NUM_BANKS-1:0] mem_atop_o, + /// Read data response + input mem_data_t [MEM_NUM_BANKS-1:0] mem_rdata_i, + /// Status output, busy flag of `axi_to_mem` + output logic [1:0] axi_to_mem_busy_o +); + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t mem_axi_req; + axi_resp_t mem_axi_resp; + + `AXI_ASSIGN_TO_REQ(mem_axi_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, mem_axi_resp) + + axi_to_mem_banked #( + .AxiIdWidth ( AXI_ID_WIDTH ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .axi_aw_chan_t ( aw_chan_t ), + .axi_w_chan_t ( w_chan_t ), + .axi_b_chan_t ( b_chan_t ), + .axi_ar_chan_t ( ar_chan_t ), + .axi_r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .MemNumBanks ( MEM_NUM_BANKS ), + .MemAddrWidth ( MEM_ADDR_WIDTH ), + .MemDataWidth ( MEM_DATA_WIDTH ), + .MemLatency ( MEM_LATENCY ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_banked ( + .clk_i, + .rst_ni, + .test_i, + .axi_to_mem_busy_o, + .axi_req_i ( mem_axi_req ), + .axi_resp_o ( mem_axi_resp ), + .mem_req_o, + .mem_gnt_i, + .mem_add_o, + .mem_wdata_o, + .mem_be_o, + .mem_atop_o, + .mem_we_o, + .mem_rdata_i + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI address width must be at least 1!"); + assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI data width must be at least 1!"); + assert (AXI_ID_WIDTH >= 1) else $fatal(1, "AXI ID width must be at least 1!"); + assert (AXI_USER_WIDTH >= 1) else $fatal(1, "AXI user width must be at least 1!"); + end +`endif +// pragma translate_on +endmodule + diff --git a/hw/vendor/pulp_platform_axi/src/axi_to_mem_interleaved.sv b/hw/vendor/pulp_platform_axi/src/axi_to_mem_interleaved.sv new file mode 100644 index 000000000..06dc1c2a4 --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_to_mem_interleaved.sv @@ -0,0 +1,368 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Wolfgang Roenninger +// - Thomas Benz +// - Michael Rogenmoser + +/// AXI4+ATOP to SRAM memory slave. Allows for parallel read and write transactions. +/// Allows reads to bypass writes, in contrast to `axi_to_mem`, however needs more hardware. +module axi_to_mem_interleaved #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// Dependent parameter, do not override. Memory address type. + parameter type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [DataWidth/NumBanks/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i +); + + // internal signals + logic w_busy, r_busy; + logic [NumBanks-1:0] arb_outcome, arb_outcome_head; + + // internal AXI buses + axi_req_t r_axi_req, w_axi_req; + axi_resp_t r_axi_resp, w_axi_resp; + + // internal TCDM buses + logic [NumBanks-1:0] r_mem_req, w_mem_req; + logic [NumBanks-1:0] r_mem_gnt, w_mem_gnt; + addr_t [NumBanks-1:0] r_mem_addr, w_mem_addr; + mem_data_t [NumBanks-1:0] r_mem_wdata, w_mem_wdata; + mem_strb_t [NumBanks-1:0] r_mem_strb, w_mem_strb; + axi_pkg::atop_t [NumBanks-1:0] r_mem_atop, w_mem_atop; + logic [NumBanks-1:0] r_mem_we, w_mem_we; + logic [NumBanks-1:0] r_mem_rvalid, w_mem_rvalid; + mem_data_t [NumBanks-1:0] r_mem_rdata, w_mem_rdata; + + // split AXI bus in read and write + axi_demux_simple #( + .AxiIdWidth ( IdWidth ), + .AtopSupport ( 1'b1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( BufDepth ), + .AxiLookBits ( 1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ) // Can be set as ports are statically selected -> reduces HW + ) i_split_read_write ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_ar_select_i ( 1'b0 ), + .slv_aw_select_i ( 1'b1 ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( {w_axi_req, r_axi_req} ), + .mst_resps_i ( {w_axi_resp, r_axi_resp} ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth( OutFifoDepth ) + ) i_axi_to_mem_write ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( w_busy ), + .axi_req_i ( w_axi_req ), + .axi_resp_o ( w_axi_resp ), + .mem_req_o ( w_mem_req ), + .mem_gnt_i ( w_mem_gnt ), + .mem_addr_o ( w_mem_addr ), + .mem_wdata_o ( w_mem_wdata ), + .mem_strb_o ( w_mem_strb ), + .mem_atop_o ( w_mem_atop ), + .mem_we_o ( w_mem_we ), + .mem_rvalid_i ( w_mem_rvalid ), + .mem_rdata_i ( w_mem_rdata ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem_read ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( r_busy ), + .axi_req_i ( r_axi_req ), + .axi_resp_o ( r_axi_resp ), + .mem_req_o ( r_mem_req ), + .mem_gnt_i ( r_mem_gnt ), + .mem_addr_o ( r_mem_addr ), + .mem_wdata_o ( r_mem_wdata ), + .mem_strb_o ( r_mem_strb ), + .mem_atop_o ( r_mem_atop ), + .mem_we_o ( r_mem_we ), + .mem_rvalid_i ( r_mem_rvalid ), + .mem_rdata_i ( r_mem_rdata ) + ); + + // create a struct for the rr-arb-tree + typedef struct packed { + addr_t addr; + mem_data_t wdata; + mem_strb_t strb; + logic we; + axi_pkg::atop_t atop; + } mem_req_payload_t; + + mem_req_payload_t [NumBanks-1:0] r_payload, w_payload, payload; + + for (genvar i = 0; i < NumBanks; i++) begin + // pack the mem + assign r_payload[i].addr = r_mem_addr[i]; + assign r_payload[i].wdata = r_mem_wdata[i]; + assign r_payload[i].strb = r_mem_strb[i]; + assign r_payload[i].we = r_mem_we[i]; + assign r_payload[i].atop = r_mem_atop[i]; + + assign w_payload[i].addr = w_mem_addr[i]; + assign w_payload[i].wdata = w_mem_wdata[i]; + assign w_payload[i].strb = w_mem_strb[i]; + assign w_payload[i].we = w_mem_we[i]; + assign w_payload[i].atop = w_mem_atop[i]; + + assign mem_addr_o [i] = payload[i].addr; + assign mem_wdata_o[i] = payload[i].wdata; + assign mem_strb_o [i] = payload[i].strb; + assign mem_we_o [i] = payload[i].we; + assign mem_atop_o [i] = payload[i].atop; + + // route data back to both channels + assign w_mem_rdata[i] = mem_rdata_i[i]; + assign r_mem_rdata[i] = mem_rdata_i[i]; + + assign w_mem_rvalid[i] = mem_rvalid_i[i] & !arb_outcome_head[i]; + assign r_mem_rvalid[i] = mem_rvalid_i[i] & arb_outcome_head[i]; + + // fine-grain arbitration + rr_arb_tree #( + .NumIn ( 2 ), + .DataType ( mem_req_payload_t ) + ) i_rr_arb_tree ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .req_i ( { r_mem_req[i], w_mem_req[i] } ), + .gnt_o ( { r_mem_gnt[i], w_mem_gnt[i] } ), + .data_i ( { r_payload[i], w_payload[i] } ), + .req_o ( mem_req_o[i] ), + .gnt_i ( mem_gnt_i[i] ), + .data_o ( payload[i] ), + .idx_o ( arb_outcome[i] ) + ); + + // back-routing store + fifo_v3 #( + .DATA_WIDTH ( 1 ), + .DEPTH ( BufDepth + 1 ) + ) i_fifo_v3_response_trgt_store ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( ), + .empty_o ( ), + .usage_o ( ), + .data_i ( arb_outcome[i] ), + .push_i ( mem_req_o[i] & mem_gnt_i[i] ), + .data_o ( arb_outcome_head[i] ), + .pop_i ( mem_rvalid_i[i] ) + ); + end + +endmodule : axi_to_mem_interleaved + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem_interleaved` +module axi_to_mem_interleaved_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'd0, + /// Number of memory banks / macros + parameter int unsigned MEM_NUM_BANKS = 32'd4, + /// Read latency of the connected memory in cycles + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + + /// Dependent parameter, do not override. Memory address type. + parameter type mem_addr_t = logic [AXI_ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. Memory atomic type. + parameter type mem_atop_t = axi_pkg::atop_t, + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [AXI_DATA_WIDTH/MEM_NUM_BANKS-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [AXI_DATA_WIDTH/MEM_NUM_BANKS/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// Status output, busy flag of `axi_to_mem` + output logic busy_o, + /// AXI4+ATOP slave port + AXI_BUS.Slave slv, + /// Memory bank request + output logic [MEM_NUM_BANKS-1:0] mem_req_o, + /// Memory request grant + input logic [MEM_NUM_BANKS-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MEM_NUM_BANKS-1:0] mem_addr_o, + /// Write data + output mem_data_t [MEM_NUM_BANKS-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MEM_NUM_BANKS-1:0] mem_strb_o, + /// Atomic operation + output mem_atop_t [MEM_NUM_BANKS-1:0] mem_atop_o, + /// Write request enable, active high + output logic [MEM_NUM_BANKS-1:0] mem_we_o, + /// Read data valid response, active high + input logic [MEM_NUM_BANKS-1:0] mem_rvalid_i, + /// Read data response + input mem_data_t [MEM_NUM_BANKS-1:0] mem_rdata_i +); + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t mem_axi_req; + axi_resp_t mem_axi_resp; + + `AXI_ASSIGN_TO_REQ(mem_axi_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, mem_axi_resp) + + axi_to_mem_interleaved #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AXI_ADDR_WIDTH ), + .DataWidth ( AXI_DATA_WIDTH ), + .IdWidth ( AXI_ID_WIDTH ), + .NumBanks ( MEM_NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_interleaved ( + .clk_i, + .rst_ni, + .test_i, + .busy_o, + .axi_req_i ( mem_axi_req ), + .axi_resp_o ( mem_axi_resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI address width must be at least 1!"); + assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI data width must be at least 1!"); + assert (AXI_ID_WIDTH >= 1) else $fatal(1, "AXI ID width must be at least 1!"); + assert (AXI_USER_WIDTH >= 1) else $fatal(1, "AXI user width must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule // axi_to_mem_interleaved_intf diff --git a/hw/vendor/pulp_platform_axi/src/axi_to_mem_split.sv b/hw/vendor/pulp_platform_axi/src/axi_to_mem_split.sv new file mode 100644 index 000000000..3e98bda2e --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_to_mem_split.sv @@ -0,0 +1,264 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Michael Rogenmoser +// - Thomas Benz + +`include "axi/assign.svh" +/// AXI4+ATOP to memory-protocol interconnect. Completely separates the read and write channel to +/// individual mem ports. This can only be used when addresses for the same bank are accessible +/// from different memory ports. +module axi_to_mem_split #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned AxiDataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Memory data width, must evenly divide `DataWidth`. + parameter int unsigned MemDataWidth = 0, // must divide `AxiDataWidth` without remainder + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 0, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// Dependent parameters, do not override. Number of memory ports. + parameter int unsigned NumMemPorts = 2*AxiDataWidth/MemDataWidth, + /// Dependent parameter, do not override. Memory address type. + parameter type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [MemDataWidth-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [MemDataWidth/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumMemPorts-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumMemPorts-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumMemPorts-1:0] mem_addr_o, // byte address + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumMemPorts-1:0] mem_wdata_o, // write data + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumMemPorts-1:0] mem_strb_o, // byte-wise strobe + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumMemPorts-1:0] mem_atop_o, // atomic operation + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumMemPorts-1:0] mem_we_o, // write enable + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumMemPorts-1:0] mem_rvalid_i, // response valid + /// Memory stream master, read response data. + input mem_data_t [NumMemPorts-1:0] mem_rdata_i // read data +); + + axi_req_t axi_read_req, axi_write_req; + axi_resp_t axi_read_resp, axi_write_resp; + + logic read_busy, write_busy; + + // split AXI bus in read and write + axi_demux_simple #( + .AxiIdWidth ( IdWidth ), + .AtopSupport ( 1'b1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( BufDepth ), + .AxiLookBits ( 1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ) // Can be set as ports are statically selected -> reduces HW + ) i_split_read_write ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_ar_select_i ( 1'b0 ), + .slv_aw_select_i ( 1'b1 ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( {axi_write_req, axi_read_req} ), + .mst_resps_i ( {axi_write_resp, axi_read_resp} ) + ); + + assign busy_o = read_busy || write_busy; + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( 1'b0 ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem_read ( + .clk_i, + .rst_ni, + .busy_o ( read_busy ), + .axi_req_i ( axi_read_req ), + .axi_resp_o ( axi_read_resp ), + .mem_req_o ( mem_req_o [NumMemPorts/2-1:0] ), + .mem_gnt_i ( mem_gnt_i [NumMemPorts/2-1:0] ), + .mem_addr_o ( mem_addr_o [NumMemPorts/2-1:0] ), + .mem_wdata_o ( mem_wdata_o [NumMemPorts/2-1:0] ), + .mem_strb_o ( mem_strb_o [NumMemPorts/2-1:0] ), + .mem_atop_o ( mem_atop_o [NumMemPorts/2-1:0] ), + .mem_we_o ( mem_we_o [NumMemPorts/2-1:0] ), + .mem_rvalid_i ( mem_rvalid_i [NumMemPorts/2-1:0] ), + .mem_rdata_i ( mem_rdata_i [NumMemPorts/2-1:0] ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem_write ( + .clk_i, + .rst_ni, + .busy_o ( write_busy ), + .axi_req_i ( axi_write_req ), + .axi_resp_o ( axi_write_resp ), + .mem_req_o ( mem_req_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_gnt_i ( mem_gnt_i [NumMemPorts-1:NumMemPorts/2] ), + .mem_addr_o ( mem_addr_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_wdata_o ( mem_wdata_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_strb_o ( mem_strb_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_atop_o ( mem_atop_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_we_o ( mem_we_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_rvalid_i ( mem_rvalid_i [NumMemPorts-1:NumMemPorts/2] ), + .mem_rdata_i ( mem_rdata_i [NumMemPorts-1:NumMemPorts/2] ) + ); + +endmodule + +`include "axi/typedef.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem_split` +module axi_to_mem_split_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'b0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'b0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'b0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'b0, + /// Memory data width, must evenly divide `DataWidth`. + parameter int unsigned MEM_DATA_WIDTH = 32'b0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 0, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + /// Dependent parameters, do not override. Number of memory ports. + parameter int unsigned NUM_MEM_PORTS = 2*AXI_DATA_WIDTH/MEM_DATA_WIDTH, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + parameter type addr_t = logic [AXI_ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + parameter type mem_data_t = logic [MEM_DATA_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + parameter type mem_strb_t = logic [MEM_DATA_WIDTH/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// See `axi_to_mem_split`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave axi_bus, + /// See `axi_to_mem_split`, port `mem_req_o`. + output logic [NUM_MEM_PORTS-1:0] mem_req_o, + /// See `axi_to_mem_split`, port `mem_gnt_i`. + input logic [NUM_MEM_PORTS-1:0] mem_gnt_i, + /// See `axi_to_mem_split`, port `mem_addr_o`. + output addr_t [NUM_MEM_PORTS-1:0] mem_addr_o, + /// See `axi_to_mem_split`, port `mem_wdata_o`. + output mem_data_t [NUM_MEM_PORTS-1:0] mem_wdata_o, + /// See `axi_to_mem_split`, port `mem_strb_o`. + output mem_strb_t [NUM_MEM_PORTS-1:0] mem_strb_o, + /// See `axi_to_mem_split`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_MEM_PORTS-1:0] mem_atop_o, + /// See `axi_to_mem_split`, port `mem_we_o`. + output logic [NUM_MEM_PORTS-1:0] mem_we_o, + /// See `axi_to_mem_split`, port `mem_rvalid_i`. + input logic [NUM_MEM_PORTS-1:0] mem_rvalid_i, + /// See `axi_to_mem_split`, port `mem_rdata_i`. + input mem_data_t [NUM_MEM_PORTS-1:0] mem_rdata_i +); + + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t axi_req; + axi_resp_t axi_resp; + `AXI_ASSIGN_TO_REQ(axi_req, axi_bus) + `AXI_ASSIGN_FROM_RESP(axi_bus, axi_resp) + + axi_to_mem_split #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .AddrWidth ( AXI_ADDR_WIDTH ), + .IdWidth ( AXI_ID_WIDTH ), + .MemDataWidth ( MEM_DATA_WIDTH ), // must divide `AxiDataWidth` without remainder + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_split ( + .clk_i, + .rst_ni, + .test_i, + .busy_o, + .axi_req_i (axi_req), + .axi_resp_o (axi_resp), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_xbar.sv b/hw/vendor/pulp_platform_axi/src/axi_xbar.sv index d66cd97d5..5d977660c 100644 --- a/hw/vendor/pulp_platform_axi/src/axi_xbar.sv +++ b/hw/vendor/pulp_platform_axi/src/axi_xbar.sv @@ -13,179 +13,120 @@ // - Andreas Kurth // - Florian Zaruba -// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. -// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. -module axi_xbar #( - parameter axi_pkg::xbar_cfg_t Cfg = '0, - parameter bit ATOPs = 1'b1, - parameter type slv_aw_chan_t = logic, - parameter type mst_aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type slv_b_chan_t = logic, - parameter type mst_b_chan_t = logic, - parameter type slv_ar_chan_t = logic, - parameter type mst_ar_chan_t = logic, - parameter type slv_r_chan_t = logic, - parameter type mst_r_chan_t = logic, - parameter type slv_req_t = logic, - parameter type slv_resp_t = logic, - parameter type mst_req_t = logic, - parameter type mst_resp_t = logic, - parameter type rule_t = axi_pkg::xbar_rule_64_t +/// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. +/// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. +module axi_xbar +import cf_math_pkg::idx_width; +#( + /// Configuration struct for the crossbar see `axi_pkg` for fields and definitions. + parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Enable atomic operations support. + parameter bit ATOPs = 1'b1, + /// Connectivity matrix + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] Connectivity = '1, + /// AXI4+ATOP AW channel struct type for the slave ports. + parameter type slv_aw_chan_t = logic, + /// AXI4+ATOP AW channel struct type for the master ports. + parameter type mst_aw_chan_t = logic, + /// AXI4+ATOP W channel struct type for all ports. + parameter type w_chan_t = logic, + /// AXI4+ATOP B channel struct type for the slave ports. + parameter type slv_b_chan_t = logic, + /// AXI4+ATOP B channel struct type for the master ports. + parameter type mst_b_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the slave ports. + parameter type slv_ar_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the master ports. + parameter type mst_ar_chan_t = logic, + /// AXI4+ATOP R channel struct type for the slave ports. + parameter type slv_r_chan_t = logic, + /// AXI4+ATOP R channel struct type for the master ports. + parameter type mst_r_chan_t = logic, + /// AXI4+ATOP request struct type for the slave ports. + parameter type slv_req_t = logic, + /// AXI4+ATOP response struct type for the slave ports. + parameter type slv_resp_t = logic, + /// AXI4+ATOP request struct type for the master ports. + parameter type mst_req_t = logic, + /// AXI4+ATOP response struct type for the master ports + parameter type mst_resp_t = logic, + /// Address rule type for the address decoders from `common_cells:addr_decode`. + /// Example types are provided in `axi_pkg`. + /// Required struct fields: + /// ``` + /// typedef struct packed { + /// int unsigned idx; + /// axi_addr_t start_addr; + /// axi_addr_t end_addr; + /// } rule_t; + /// ``` + parameter type rule_t = axi_pkg::xbar_rule_64_t +`ifdef VCS + , localparam int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +`endif ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, - input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, - output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, - output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, - input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, - input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, - input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, - input logic [Cfg.NoSlvPorts-1:0][$clog2(Cfg.NoMstPorts)-1:0] default_mst_port_i + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable, active high. + input logic test_i, + /// AXI4+ATOP requests to the slave ports. + input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + /// AXI4+ATOP responses of the slave ports. + output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + /// AXI4+ATOP requests of the master ports. + output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + /// AXI4+ATOP responses to the master ports. + input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + /// Address map array input for the crossbar. This map is global for the whole module. + /// It is used for routing the transactions to the respective master ports. + /// Each master port can have multiple different rules. + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + /// Enable default master port. + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, +`ifdef VCS + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif ); - typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; - // to account for the decoding error slave - typedef logic [$clog2(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; - - // signals from the axi_demuxes, one index more for decode error - slv_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; - slv_resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; - - // workaround for issue #133 (problem with vsim 10.6c) - localparam int unsigned cfg_NoMstPorts = Cfg.NoMstPorts; - // signals into the axi_muxes, are of type slave as the multiplexer extends the ID slv_req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; slv_resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; - for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux - logic [$clog2(Cfg.NoMstPorts)-1:0] dec_aw, dec_ar; - mst_port_idx_t slv_aw_select, slv_ar_select; - logic dec_aw_valid, dec_aw_error; - logic dec_ar_valid, dec_ar_error; - - addr_decode #( - .NoIndices ( Cfg.NoMstPorts ), - .NoRules ( Cfg.NoAddrRules ), - .addr_t ( addr_t ), - .rule_t ( rule_t ) - ) i_axi_aw_decode ( - .addr_i ( slv_ports_req_i[i].aw.addr ), - .addr_map_i ( addr_map_i ), - .idx_o ( dec_aw ), - .dec_valid_o ( dec_aw_valid ), - .dec_error_o ( dec_aw_error ), - .en_default_idx_i ( en_default_mst_port_i[i] ), - .default_idx_i ( default_mst_port_i[i] ) - ); - - addr_decode #( - .NoIndices ( Cfg.NoMstPorts ), - .addr_t ( addr_t ), - .NoRules ( Cfg.NoAddrRules ), - .rule_t ( rule_t ) - ) i_axi_ar_decode ( - .addr_i ( slv_ports_req_i[i].ar.addr ), - .addr_map_i ( addr_map_i ), - .idx_o ( dec_ar ), - .dec_valid_o ( dec_ar_valid ), - .dec_error_o ( dec_ar_error ), - .en_default_idx_i ( en_default_mst_port_i[i] ), - .default_idx_i ( default_mst_port_i[i] ) - ); - - assign slv_aw_select = (dec_aw_error) ? - mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_aw); - assign slv_ar_select = (dec_ar_error) ? - mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_ar); - - // make sure that the default slave does not get changed, if there is an unserved Ax - // pragma translate_off - `ifndef VERILATOR - `ifndef XSIM - default disable iff (~rst_ni); - default_aw_mst_port_en: assert property( - @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) - |=> $stable(en_default_mst_port_i[i])) - else $fatal (1, $sformatf("It is not allowed to change the default mst port\ - enable, when there is an unserved Aw beat. Slave Port: %0d", i)); - default_aw_mst_port: assert property( - @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) - |=> $stable(default_mst_port_i[i])) - else $fatal (1, $sformatf("It is not allowed to change the default mst port\ - when there is an unserved Aw beat. Slave Port: %0d", i)); - default_ar_mst_port_en: assert property( - @(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready) - |=> $stable(en_default_mst_port_i[i])) - else $fatal (1, $sformatf("It is not allowed to change the enable, when\ - there is an unserved Ar beat. Slave Port: %0d", i)); - default_ar_mst_port: assert property( - @(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready) - |=> $stable(default_mst_port_i[i])) - else $fatal (1, $sformatf("It is not allowed to change the default mst port\ - when there is an unserved Ar beat. Slave Port: %0d", i)); - `endif - `endif - // pragma translate_on - axi_demux #( - .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), // ID Width - .aw_chan_t ( slv_aw_chan_t ), // AW Channel Type - .w_chan_t ( w_chan_t ), // W Channel Type - .b_chan_t ( slv_b_chan_t ), // B Channel Type - .ar_chan_t ( slv_ar_chan_t ), // AR Channel Type - .r_chan_t ( slv_r_chan_t ), // R Channel Type - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ), - .NoMstPorts ( Cfg.NoMstPorts + 1 ), - .MaxTrans ( Cfg.MaxMstTrans ), - .AxiLookBits ( Cfg.AxiIdUsedSlvPorts ), - .UniqueIds ( Cfg.UniqueIds ), - .FallThrough ( Cfg.FallThrough ), - .SpillAw ( Cfg.LatencyMode[9] ), - .SpillW ( Cfg.LatencyMode[8] ), - .SpillB ( Cfg.LatencyMode[7] ), - .SpillAr ( Cfg.LatencyMode[6] ), - .SpillR ( Cfg.LatencyMode[5] ) - ) i_axi_demux ( - .clk_i, // Clock - .rst_ni, // Asynchronous reset active low - .test_i, // Testmode enable - .slv_req_i ( slv_ports_req_i[i] ), - .slv_aw_select_i ( slv_aw_select ), - .slv_ar_select_i ( slv_ar_select ), - .slv_resp_o ( slv_ports_resp_o[i] ), - .mst_reqs_o ( slv_reqs[i] ), - .mst_resps_i ( slv_resps[i] ) - ); - - axi_err_slv #( - .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ), - .Resp ( axi_pkg::RESP_DECERR ), - .ATOPs ( ATOPs ), - .MaxTrans ( 4 ) // Transactions terminate at this slave, so minimize - // resource consumption by accepting only a few - // transactions at a time. - ) i_axi_err_slv ( - .clk_i, // Clock - .rst_ni, // Asynchronous reset active low - .test_i, // Testmode enable - // slave port - .slv_req_i ( slv_reqs[i][Cfg.NoMstPorts] ), - .slv_resp_o ( slv_resps[i][cfg_NoMstPorts] ) - ); - end - - // cross all channels - for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_xbar_slv_cross - for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross - assign mst_reqs[j][i] = slv_reqs[i][j]; - assign slv_resps[i][j] = mst_resps[j][i]; - end - end + axi_xbar_unmuxed #( + .Cfg (Cfg), + .ATOPs (ATOPs), + .Connectivity (Connectivity), + .aw_chan_t (slv_aw_chan_t), + .w_chan_t (w_chan_t), + .b_chan_t (slv_b_chan_t), + .ar_chan_t (slv_ar_chan_t), + .r_chan_t (slv_r_chan_t), + .req_t (slv_req_t), + .resp_t (slv_resp_t), + .rule_t (rule_t) + ) i_xbar_unmuxed ( + .clk_i, + .rst_ni, + .test_i, + .slv_ports_req_i, + .slv_ports_resp_o, + .mst_ports_req_o (mst_reqs), + .mst_ports_resp_i (mst_resps), + .addr_map_i, + .en_default_mst_port_i, + .default_mst_port_i + ); for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_mst_port_mux axi_mux #( @@ -239,19 +180,31 @@ endmodule `include "axi/assign.svh" `include "axi/typedef.svh" -module axi_xbar_intf #( +module axi_xbar_intf +import cf_math_pkg::idx_width; +#( parameter int unsigned AXI_USER_WIDTH = 0, parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter bit ATOPS = 1'b1, + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] CONNECTIVITY = '1, parameter type rule_t = axi_pkg::xbar_rule_64_t +`ifdef VCS + , localparam int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +`endif ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, - AXI_BUS.Slave slv_ports [Cfg.NoSlvPorts-1:0], - AXI_BUS.Master mst_ports [Cfg.NoMstPorts-1:0], - input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, - input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, - input logic [Cfg.NoSlvPorts-1:0][$clog2(Cfg.NoMstPorts)-1:0] default_mst_port_i + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv_ports [Cfg.NoSlvPorts-1:0], + AXI_BUS.Master mst_ports [Cfg.NoMstPorts-1:0], + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, +`ifdef VCS + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif ); localparam int unsigned AxiIdWidthMstPorts = Cfg.AxiIdWidthSlvPorts + $clog2(Cfg.NoSlvPorts); @@ -294,6 +247,8 @@ module axi_xbar_intf #( axi_xbar #( .Cfg (Cfg), + .ATOPs ( ATOPS ), + .Connectivity ( CONNECTIVITY ), .slv_aw_chan_t ( slv_aw_chan_t ), .mst_aw_chan_t ( mst_aw_chan_t ), .w_chan_t ( w_chan_t ), diff --git a/hw/vendor/pulp_platform_axi/src/axi_xbar_unmuxed.sv b/hw/vendor/pulp_platform_axi/src/axi_xbar_unmuxed.sv new file mode 100644 index 000000000..32679900f --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_xbar_unmuxed.sv @@ -0,0 +1,369 @@ +// Copyright (c) 2019 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Wolfgang Roenninger +// - Andreas Kurth +// - Florian Zaruba + +/// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. +/// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. +module axi_xbar_unmuxed +import cf_math_pkg::idx_width; +#( + /// Configuration struct for the crossbar see `axi_pkg` for fields and definitions. + parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Enable atomic operations support. + parameter bit ATOPs = 1'b1, + /// Connectivity matrix + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] Connectivity = '1, + /// AXI4+ATOP AW channel struct type for the slave ports. + parameter type aw_chan_t = logic, + /// AXI4+ATOP W channel struct type for all ports. + parameter type w_chan_t = logic, + /// AXI4+ATOP B channel struct type for the slave ports. + parameter type b_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the slave ports. + parameter type ar_chan_t = logic, + /// AXI4+ATOP R channel struct type for the slave ports. + parameter type r_chan_t = logic, + /// AXI4+ATOP request struct type for the slave ports. + parameter type req_t = logic, + /// AXI4+ATOP response struct type for the slave ports. + parameter type resp_t = logic, + /// Address rule type for the address decoders from `common_cells:addr_decode`. + /// Example types are provided in `axi_pkg`. + /// Required struct fields: + /// ``` + /// typedef struct packed { + /// int unsigned idx; + /// axi_addr_t start_addr; + /// axi_addr_t end_addr; + /// } rule_t; + /// ``` + parameter type rule_t = axi_pkg::xbar_rule_64_t +`ifdef VCS + , localparam int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +`endif +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable, active high. + input logic test_i, + /// AXI4+ATOP requests to the slave ports. + input req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + /// AXI4+ATOP responses of the slave ports. + output resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + /// AXI4+ATOP requests of the master ports. + output req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_ports_req_o, + /// AXI4+ATOP responses to the master ports. + input resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_ports_resp_i, + /// Address map array input for the crossbar. This map is global for the whole module. + /// It is used for routing the transactions to the respective master ports. + /// Each master port can have multiple different rules. + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + /// Enable default master port. + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, +`ifdef VCS + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif +); + + // Address tpye for inidvidual address signals + typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; + // to account for the decoding error slave +`ifdef VCS + localparam int unsigned MstPortsIdxWidthOne = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts + 1)); + typedef logic [MstPortsIdxWidthOne-1:0] mst_port_idx_t; +`else + typedef logic [idx_width(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; +`endif + + // signals from the axi_demuxes, one index more for decode error + req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; + resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; + + // workaround for issue #133 (problem with vsim 10.6c) + localparam int unsigned cfg_NoMstPorts = Cfg.NoMstPorts; + + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux +`ifdef VCS + logic [MstPortsIdxWidth-1:0] dec_aw, dec_ar; +`else + logic [idx_width(Cfg.NoMstPorts)-1:0] dec_aw, dec_ar; +`endif + mst_port_idx_t slv_aw_select, slv_ar_select; + logic dec_aw_valid, dec_aw_error; + logic dec_ar_valid, dec_ar_error; + + addr_decode #( + .NoIndices ( Cfg.NoMstPorts ), + .NoRules ( Cfg.NoAddrRules ), + .addr_t ( addr_t ), + .rule_t ( rule_t ) + ) i_axi_aw_decode ( + .addr_i ( slv_ports_req_i[i].aw.addr ), + .addr_map_i ( addr_map_i ), + .idx_o ( dec_aw ), + .dec_valid_o ( dec_aw_valid ), + .dec_error_o ( dec_aw_error ), + .en_default_idx_i ( en_default_mst_port_i[i] ), + .default_idx_i ( default_mst_port_i[i] ) + ); + + addr_decode #( + .NoIndices ( Cfg.NoMstPorts ), + .addr_t ( addr_t ), + .NoRules ( Cfg.NoAddrRules ), + .rule_t ( rule_t ) + ) i_axi_ar_decode ( + .addr_i ( slv_ports_req_i[i].ar.addr ), + .addr_map_i ( addr_map_i ), + .idx_o ( dec_ar ), + .dec_valid_o ( dec_ar_valid ), + .dec_error_o ( dec_ar_error ), + .en_default_idx_i ( en_default_mst_port_i[i] ), + .default_idx_i ( default_mst_port_i[i] ) + ); + + assign slv_aw_select = (dec_aw_error) ? + mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_aw); + assign slv_ar_select = (dec_ar_error) ? + mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_ar); + + // make sure that the default slave does not get changed, if there is an unserved Ax + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + default disable iff (~rst_ni); + default_aw_mst_port_en: assert property( + @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) + |=> $stable(en_default_mst_port_i[i])) + else $fatal (1, $sformatf("It is not allowed to change the default mst port\ + enable, when there is an unserved Aw beat. Slave Port: %0d", i)); + default_aw_mst_port: assert property( + @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) + |=> $stable(default_mst_port_i[i])) + else $fatal (1, $sformatf("It is not allowed to change the default mst port\ + when there is an unserved Aw beat. Slave Port: %0d", i)); + default_ar_mst_port_en: assert property( + @(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready) + |=> $stable(en_default_mst_port_i[i])) + else $fatal (1, $sformatf("It is not allowed to change the enable, when\ + there is an unserved Ar beat. Slave Port: %0d", i)); + default_ar_mst_port: assert property( + @(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready) + |=> $stable(default_mst_port_i[i])) + else $fatal (1, $sformatf("It is not allowed to change the default mst port\ + when there is an unserved Ar beat. Slave Port: %0d", i)); + `endif + `endif + // pragma translate_on + axi_demux #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), // ID Width + .AtopSupport ( ATOPs ), + .aw_chan_t ( aw_chan_t ), // AW Channel Type + .w_chan_t ( w_chan_t ), // W Channel Type + .b_chan_t ( b_chan_t ), // B Channel Type + .ar_chan_t ( ar_chan_t ), // AR Channel Type + .r_chan_t ( r_chan_t ), // R Channel Type + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .NoMstPorts ( Cfg.NoMstPorts + 1 ), + .MaxTrans ( Cfg.MaxMstTrans ), + .AxiLookBits ( Cfg.AxiIdUsedSlvPorts ), + .UniqueIds ( Cfg.UniqueIds ), + .SpillAw ( Cfg.LatencyMode[9] ), + .SpillW ( Cfg.LatencyMode[8] ), + .SpillB ( Cfg.LatencyMode[7] ), + .SpillAr ( Cfg.LatencyMode[6] ), + .SpillR ( Cfg.LatencyMode[5] ) + ) i_axi_demux ( + .clk_i, // Clock + .rst_ni, // Asynchronous reset active low + .test_i, // Testmode enable + .slv_req_i ( slv_ports_req_i[i] ), + .slv_aw_select_i ( slv_aw_select ), + .slv_ar_select_i ( slv_ar_select ), + .slv_resp_o ( slv_ports_resp_o[i] ), + .mst_reqs_o ( slv_reqs[i] ), + .mst_resps_i ( slv_resps[i] ) + ); + + axi_err_slv #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .ATOPs ( ATOPs ), + .MaxTrans ( 4 ) // Transactions terminate at this slave, so minimize + // resource consumption by accepting only a few + // transactions at a time. + ) i_axi_err_slv ( + .clk_i, // Clock + .rst_ni, // Asynchronous reset active low + .test_i, // Testmode enable + // slave port + .slv_req_i ( slv_reqs[i][Cfg.NoMstPorts] ), + .slv_resp_o ( slv_resps[i][cfg_NoMstPorts] ) + ); + end + + // cross all channels + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_xbar_slv_cross + for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross + if (Connectivity[i][j]) begin : gen_connection + axi_multicut #( + .NoCuts ( Cfg.PipelineStages ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ) + ) i_axi_multicut_xbar_pipeline ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ), + .mst_req_o ( mst_ports_req_o[j][i] ), + .mst_resp_i ( mst_ports_resp_i[j][i] ) + ); + + end else begin : gen_no_connection + assign mst_ports_req_o[j][i] = '0; + axi_err_slv #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .ATOPs ( ATOPs ), + .MaxTrans ( 1 ) + ) i_axi_err_slv ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ) + ); + end + end + end + + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + initial begin : check_params + id_slv_req_ports: assert ($bits(slv_ports_req_i[0].aw.id ) == Cfg.AxiIdWidthSlvPorts) else + $fatal(1, $sformatf("Slv_req and aw_chan id width not equal.")); + id_slv_resp_ports: assert ($bits(slv_ports_resp_o[0].r.id) == Cfg.AxiIdWidthSlvPorts) else + $fatal(1, $sformatf("Slv_req and aw_chan id width not equal.")); + end + `endif + `endif + // pragma translate_on +endmodule + +`ifndef VCS +// As of now, VCS does not support multi-dimensional array of interfaces. +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_xbar_unmuxed_intf +import cf_math_pkg::idx_width; +#( + parameter int unsigned AXI_USER_WIDTH = 0, + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter bit ATOPS = 1'b1, + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] CONNECTIVITY = '1, + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv_ports [Cfg.NoSlvPorts-1:0], + AXI_BUS.Master mst_ports [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0], + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +); + + typedef logic [Cfg.AxiIdWidthSlvPorts -1:0] id_t; + typedef logic [Cfg.AxiAddrWidth -1:0] addr_t; + typedef logic [Cfg.AxiDataWidth -1:0] data_t; + typedef logic [Cfg.AxiDataWidth/8 -1:0] strb_t; + typedef logic [AXI_USER_WIDTH -1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + + req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; + resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; + req_t [Cfg.NoSlvPorts-1:0] slv_reqs; + resp_t [Cfg.NoSlvPorts-1:0] slv_resps; + + for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_assign_mst + for (genvar j = 0; j < Cfg.NoSlvPorts; j++) begin : gen_assign_mst_inner + `AXI_ASSIGN_FROM_REQ(mst_ports[i][j], mst_reqs[i][j]) + `AXI_ASSIGN_TO_RESP(mst_resps[i][j], mst_ports[i][j]) + end + end + + for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_assign_slv + `AXI_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i]) + `AXI_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i]) + end + + axi_xbar_unmuxed #( + .Cfg ( Cfg ), + .ATOPs ( ATOPS ), + .Connectivity ( CONNECTIVITY ), + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .req_t ( req_t ), + .resp_t ( resp_t ), + .rule_t ( rule_t ) + ) i_xbar ( + .clk_i, + .rst_ni, + .test_i, + .slv_ports_req_i (slv_reqs ), + .slv_ports_resp_o (slv_resps), + .mst_ports_req_o (mst_reqs ), + .mst_ports_resp_i (mst_resps), + .addr_map_i, + .en_default_mst_port_i, + .default_mst_port_i + ); + +endmodule + +`endif diff --git a/hw/vendor/pulp_platform_axi/src/axi_xp.sv b/hw/vendor/pulp_platform_axi/src/axi_xp.sv new file mode 100644 index 000000000..5ccbfb9ff --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_xp.sv @@ -0,0 +1,256 @@ +// Copyright (c) 2020 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Tim Fischer +// - Andreas Kurth +// - Vikram Jain + +`include "axi/typedef.svh" + +/// AXI Crosspoint (XP) with homomorphous slave and master ports. +module axi_xp #( + // Atomic operations settings + parameter bit ATOPs = 1'b1, + // xbar configuration + parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Number of slave ports. + parameter int unsigned NumSlvPorts = 32'd0, + /// Number of master ports. + parameter int unsigned NumMstPorts = 32'd0, + /// Connectivity from a slave port to the master ports. A `1'b1` in `Connectivity[i][j]` means + /// that slave port `i` is connected to master port `j`. By default, all slave ports are + /// connected to all master ports. + parameter bit [NumSlvPorts-1:0][NumMstPorts-1:0] Connectivity = '1, + /// Address width of all ports. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width of all ports. + parameter int unsigned AxiDataWidth = 32'd0, + /// ID width of all ports. + parameter int unsigned AxiIdWidth = 32'd0, + /// User signal width of all ports. + parameter int unsigned AxiUserWidth = 32'd0, + /// Maximum number of different IDs that can be in flight at each slave port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// It is legal for upstream to have transactions with more unique IDs than the maximum given by + /// this parameter in flight, but a transaction exceeding the maximum will be stalled until all + /// transactions of another ID complete. + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the slave port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to [`axi_id_remap` as `AxiMaxTxnsPerId` + /// parameter](module.axi_id_remap#parameter.AxiMaxTxnsPerId). + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + /// Maximum number of in-flight transactions at the slave port. Reads and writes are counted + /// separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiSlvPortMaxTxns). + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + /// Maximum number of different IDs that can be in flight at the master port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxUniqIds). + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the master port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxTxnsPerId). + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + /// Number of rules in the address map. + parameter int unsigned NumAddrRules = 32'd0, + /// Request struct type of the AXI4+ATOP + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP + parameter type axi_resp_t = logic, + /// Rule type (see documentation of `axi_xbar` for details). + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Test mode enable + input logic test_en_i, + /// Slave ports request + input axi_req_t [NumSlvPorts-1:0] slv_req_i, + /// Slave ports response + output axi_resp_t [NumSlvPorts-1:0] slv_resp_o, + /// Master ports request + output axi_req_t [NumMstPorts-1:0] mst_req_o, + /// Master ports response + input axi_resp_t [NumMstPorts-1:0] mst_resp_i, + /// Address map for transferring transactions from slave to master ports + input rule_t [NumAddrRules-1:0] addr_map_i +); + + // The master port of the Xbar has a different ID width than the slave ports. + parameter int unsigned AxiXbarIdWidth = AxiIdWidth + $clog2(NumSlvPorts); + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiIdWidth-1:0] id_t; + typedef logic [AxiXbarIdWidth-1:0] xbar_id_t; + typedef logic [AxiDataWidth/8-1:0] strb_t; + typedef logic [AxiUserWidth-1:0] user_t; + + + `AXI_TYPEDEF_ALL(xp, addr_t, id_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_ALL(xbar, addr_t, xbar_id_t, data_t, strb_t, user_t) + + xbar_req_t [NumMstPorts-1:0] xbar_req; + xbar_resp_t [NumMstPorts-1:0] xbar_resp; + + axi_xbar #( + .Cfg ( Cfg ), + .ATOPs ( ATOPs ), + .Connectivity ( Connectivity ), + .slv_aw_chan_t ( xp_aw_chan_t ), + .mst_aw_chan_t ( xbar_aw_chan_t ), + .w_chan_t ( xp_w_chan_t ), + .slv_b_chan_t ( xp_b_chan_t ), + .mst_b_chan_t ( xbar_b_chan_t ), + .slv_ar_chan_t ( xp_ar_chan_t ), + .mst_ar_chan_t ( xbar_ar_chan_t ), + .slv_r_chan_t ( xp_r_chan_t ), + .mst_r_chan_t ( xbar_r_chan_t ), + .slv_req_t ( axi_req_t ), + .slv_resp_t ( axi_resp_t ), + .mst_req_t ( xbar_req_t ), + .mst_resp_t ( xbar_resp_t ), + .rule_t ( rule_t ) + ) i_xbar ( + .clk_i, + .rst_ni, + .test_i ( test_en_i ), + .slv_ports_req_i ( slv_req_i ), + .slv_ports_resp_o ( slv_resp_o ), + .mst_ports_req_o ( xbar_req ), + .mst_ports_resp_i ( xbar_resp ), + .addr_map_i, + .en_default_mst_port_i ( '0 ), + .default_mst_port_i ( '0 ) + ); + + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_remap + axi_id_remap #( + .AxiSlvPortIdWidth ( AxiXbarIdWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiMstPortIdWidth ( AxiIdWidth ), + .slv_req_t ( xbar_req_t ), + .slv_resp_t ( xbar_resp_t ), + .mst_req_t ( axi_req_t ), + .mst_resp_t ( axi_resp_t ) + ) i_axi_id_remap ( + .clk_i, + .rst_ni, + .slv_req_i ( xbar_req[i] ), + .slv_resp_o ( xbar_resp[i] ), + .mst_req_o ( mst_req_o[i] ), + .mst_resp_i ( mst_resp_i[i] ) + ); + end + +endmodule + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_xp_intf +import cf_math_pkg::idx_width; +#( + parameter bit ATOPs = 1'b1, + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter int unsigned NumSlvPorts = 32'd0, + parameter int unsigned NumMstPorts = 32'd0, + parameter bit [NumSlvPorts-1:0][NumMstPorts-1:0] Connectivity = '1, + parameter int unsigned AxiAddrWidth = 32'd0, + parameter int unsigned AxiDataWidth = 32'd0, + parameter int unsigned AxiIdWidth = 32'd0, + parameter int unsigned AxiUserWidth = 32'd0, + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + parameter int unsigned NumAddrRules = 32'd0, + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + input logic clk_i, + input logic rst_ni, + input logic test_en_i, + AXI_BUS.Slave slv_ports [NumSlvPorts-1:0], + AXI_BUS.Master mst_ports [NumMstPorts-1:0], + input rule_t [NumAddrRules-1:0] addr_map_i +); + + // localparam int unsigned AxiIdWidthMstPorts = AxiIdWidth + $clog2(NoSlvPorts); + + typedef logic [AxiIdWidth -1:0] id_t; + typedef logic [AxiAddrWidth -1:0] addr_t; + typedef logic [AxiDataWidth -1:0] data_t; + typedef logic [AxiDataWidth/8 -1:0] strb_t; + typedef logic [AxiUserWidth -1:0] user_t; + + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t [NumMstPorts-1:0] mst_reqs; + axi_resp_t [NumMstPorts-1:0] mst_resps; + axi_req_t [NumSlvPorts-1:0] slv_reqs; + axi_resp_t [NumSlvPorts-1:0] slv_resps; + + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_assign_mst + `AXI_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) + `AXI_ASSIGN_TO_RESP(mst_resps[i], mst_ports[i]) + end + + for (genvar i = 0; i < NumSlvPorts; i++) begin : gen_assign_slv + `AXI_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i]) + `AXI_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i]) + end + + axi_xp #( + .ATOPs ( ATOPs ), + .Cfg ( Cfg ), + .NumSlvPorts ( NumSlvPorts ), + .NumMstPorts ( NumMstPorts ), + .Connectivity ( Connectivity ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiSlvPortMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ), + .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), + .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), + .NumAddrRules ( NumAddrRules ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .rule_t ( rule_t ) + ) i_xp ( + .clk_i, + .rst_ni, + .test_en_i, + .slv_req_i (slv_reqs ), + .slv_resp_o (slv_resps), + .mst_req_o (mst_reqs ), + .mst_resp_i (mst_resps), + .addr_map_i + ); + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src/axi_zero_mem.sv b/hw/vendor/pulp_platform_axi/src/axi_zero_mem.sv new file mode 100644 index 000000000..1cb48b27b --- /dev/null +++ b/hw/vendor/pulp_platform_axi/src/axi_zero_mem.sv @@ -0,0 +1,104 @@ +// Copyright (c) 2024 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Gianna Paulin +// - Thomas Benz + +`include "common_cells/registers.svh" +/// AXI4+ATOP slave module which translates AXI bursts into a memory stream +/// which behaves as a memory containing only `0` data which cannot be +/// overwritten by `write` operations. +/// - `read`: grants the request and returns data `0`. +/// - `write`: grants the request, write data goes into nothingness (can be used as data sink) +/// If both read and write channels of the AXI4+ATOP are active, both will have an +/// utilization of 50%. +module axi_zero_mem #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Dependent parameter, do not override. Memory address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + localparam type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + localparam type mem_strb_t = logic [DataWidth/NumBanks/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o +); + + logic zero_mem_gnt, zero_mem_req, zero_mem_we; + logic zero_mem_valid_req_d, zero_mem_valid_req_q; + + axi_to_detailed_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( 32'd1 ), + .BufDepth ( BufDepth ), + .HideStrb ( 1'b0 ), + .OutFifoDepth( 32'd1 ) + ) i_axi_to_detailed_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i, + .axi_resp_o, + .mem_req_o ( zero_mem_req ), + .mem_gnt_i ( zero_mem_gnt ), + .mem_addr_o ( /* NC */ ), + .mem_wdata_o ( /* NC */ ), + .mem_strb_o ( /* NC */ ), + .mem_atop_o ( /* NC */ ), + .mem_lock_o ( /* NC */ ), + .mem_we_o ( /* NC */ ), + .mem_id_o ( /* NC */ ), + .mem_user_o ( /* NC */ ), + .mem_cache_o ( /* NC */ ), + .mem_prot_o ( /* NC */ ), + .mem_qos_o ( /* NC */ ), + .mem_region_o ( /* NC */ ), + .mem_rvalid_i ( zero_mem_valid_req_q ), + .mem_rdata_i ( '0 ), + .mem_err_i ( 1'b0 ), + .mem_exokay_i ( 1'b0 ) + ); + + assign zero_mem_gnt = zero_mem_req; + assign zero_mem_valid_req_d = zero_mem_gnt & zero_mem_req; + + `FF(zero_mem_valid_req_q, zero_mem_valid_req_d, '0, clk_i, rst_ni) + +endmodule diff --git a/hw/vendor/pulp_platform_axi/src_files.yml b/hw/vendor/pulp_platform_axi/src_files.yml index ba712cd1b..451c85658 100644 --- a/hw/vendor/pulp_platform_axi/src_files.yml +++ b/hw/vendor/pulp_platform_axi/src_files.yml @@ -16,18 +16,24 @@ axi: # Level 2 - src/axi_atop_filter.sv - src/axi_burst_splitter.sv + - src/axi_bus_compare.sv - src/axi_cdc_dst.sv - src/axi_cdc_src.sv - src/axi_cut.sv - src/axi_delayer.sv - - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv + - src/axi_fifo.sv + - src/axi_id_remap.sv - src/axi_id_prepend.sv - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv + - src/axi_lite_from_mem.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -35,19 +41,41 @@ axi: - src/axi_lite_to_axi.sv - src/axi_modify_address.sv - src/axi_mux.sv + - src/axi_rw_join.sv + - src/axi_rw_split.sv - src/axi_serializer.sv + - src/axi_slave_compare.sv + - src/axi_throttle.sv + - src/axi_to_detailed_mem.sv # Level 3 - src/axi_cdc.sv + - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv + - src/axi_from_mem.sv + - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv + - src/axi_to_mem.sv + - src/axi_zero_mem.sv # Level 4 + - src/axi_interleaved_xbar.sv + - src/axi_iw_converter.sv - src/axi_lite_xbar.sv + - src/axi_xbar_unmuxed.sv + - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv + # Level 5 - src/axi_xbar.sv + # Level 6 + - src/axi_xp.sv axi_sim: files: + - src/axi_chan_compare.sv + - src/axi_dumper.sv - src/axi_sim_mem.sv - src/axi_test.sv flags: diff --git a/hw/vendor/pulp_platform_axi/test/axi_synth_bench.sv b/hw/vendor/pulp_platform_axi/test/axi_synth_bench.sv deleted file mode 100644 index 16ac9438b..000000000 --- a/hw/vendor/pulp_platform_axi/test/axi_synth_bench.sv +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger -// - Andreas Kurth -// - Fabian Schuiki - -/// A synthesis test bench which instantiates various adapter variants. -module axi_synth_bench ( - input logic clk_i, - input logic rst_ni -); - - localparam int AXI_ADDR_WIDTH[6] = '{32, 64, 1, 2, 42, 129}; - localparam int AXI_ID_USER_WIDTH[3] = '{0, 1, 8}; - localparam int NUM_SLAVE_MASTER[3] = '{1, 2, 4}; - - // AXI_DATA_WIDTH = {8, 16, 32, 64, 128, 256, 512, 1024} - for (genvar i = 0; i < 8; i++) begin - localparam DW = (2**i) * 8; - synth_slice #(.AW(32), .DW(DW), .IW(8), .UW(8)) s(.*); - end - - // AXI_ADDR_WIDTH - for (genvar i = 0; i < 6; i++) begin - localparam int AW = AXI_ADDR_WIDTH[i]; - synth_slice #(.AW(AW), .DW(32), .IW(8), .UW(8)) s(.*); - end - - // AXI_ID_WIDTH and AXI_USER_WIDTH - for (genvar i = 0; i < 3; i++) begin - localparam int UW = AXI_ID_USER_WIDTH[i]; - localparam int IW = (UW == 0) ? 1 : UW; - synth_slice #(.AW(32), .DW(32), .IW(IW), .UW(UW)) s(.*); - end - - // ATOP Filter - for (genvar iID = 1; iID <= 8; iID++) begin - localparam int IW = iID; - for (genvar iTxn = 1; iTxn <= 12; iTxn++) begin - localparam int WT = iTxn; - synth_axi_atop_filter #( - .AXI_ADDR_WIDTH (64), - .AXI_DATA_WIDTH (64), - .AXI_ID_WIDTH (IW), - .AXI_USER_WIDTH (4), - .AXI_MAX_WRITE_TXNS (WT) - ) i_filter (.*); - end - end - - // AXI4-Lite crossbar - for (genvar i = 0; i < 3; i++) begin - synth_axi_lite_xbar #( - .NoSlvMst ( NUM_SLAVE_MASTER[i] ) - ) i_lite_xbar (.*); - end - - // Clock Domain Crossing - for (genvar i = 0; i < 6; i++) begin - localparam int AW = AXI_ADDR_WIDTH[i]; - for (genvar j = 0; j < 3; j++) begin - localparam IUW = AXI_ID_USER_WIDTH[j]; - synth_axi_cdc #( - .AXI_ADDR_WIDTH (AW), - .AXI_DATA_WIDTH (128), - .AXI_ID_WIDTH (IUW), - .AXI_USER_WIDTH (IUW) - ) i_cdc (.*); - end - end - - // AXI4-Lite to APB bridge - for (genvar i_data = 0; i_data < 3; i_data++) begin - localparam int unsigned DataWidth = (2**i_data) * 8; - for (genvar i_slv = 0; i_slv < 3; i_slv++) begin - synth_axi_lite_to_apb #( - .NoApbSlaves ( NUM_SLAVE_MASTER[i_slv] ), - .DataWidth ( DataWidth ) - ) i_axi_lite_to_apb (.*); - end - end - - // AXI4-Lite Mailbox - for (genvar i_irq_mode = 0; i_irq_mode < 4; i_irq_mode++) begin - localparam bit EDGE_TRIG = i_irq_mode[0]; - localparam bit ACT_HIGH = i_irq_mode[1]; - for (genvar i_depth = 2; i_depth < 8; i_depth++) begin - localparam int unsigned DEPTH = 2**i_depth; - synth_axi_lite_mailbox #( - .MAILBOX_DEPTH ( DEPTH ), - .IRQ_EDGE_TRIG ( EDGE_TRIG ), - .IRQ_ACT_HIGH ( ACT_HIGH ) - ) i_axi_lite_mailbox (.*); - end - end - - // AXI Isolation module - for (genvar i = 0; i < 6; i++) begin - synth_axi_isolate #( - .NumPending ( AXI_ADDR_WIDTH[i] ), - .AxiIdWidth ( 32'd10 ), - .AxiAddrWidth ( 32'd64 ), - .AxiDataWidth ( 32'd512 ), - .AxiUserWidth ( 32'd10 ) - ) i_synth_axi_isolate (.*); - end - - for (genvar i = 0; i < 6; i++) begin - localparam int unsigned SLV_PORT_ADDR_WIDTH = AXI_ADDR_WIDTH[i]; - if (SLV_PORT_ADDR_WIDTH > 12) begin - for (genvar j = 0; j < 6; j++) begin - localparam int unsigned MST_PORT_ADDR_WIDTH = AXI_ADDR_WIDTH[j]; - if (MST_PORT_ADDR_WIDTH > 12) begin - synth_axi_modify_address #( - .AXI_SLV_PORT_ADDR_WIDTH (SLV_PORT_ADDR_WIDTH), - .AXI_MST_PORT_ADDR_WIDTH (MST_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (128), - .AXI_ID_WIDTH (5), - .AXI_USER_WIDTH (2) - ) i_synth_axi_modify_address (); - end - end - end - end - - // AXI4+ATOP serializer - for (genvar i = 0; i < 6; i++) begin - synth_axi_serializer #( - .NumPending ( AXI_ADDR_WIDTH[i] ), - .AxiIdWidth ( 32'd10 ), - .AxiAddrWidth ( 32'd64 ), - .AxiDataWidth ( 32'd512 ), - .AxiUserWidth ( 32'd10 ) - ) i_synth_axi_serializer (.*); - end - - // AXI4-Lite Registers - for (genvar i = 0; i < 6; i++) begin - localparam int unsigned NUM_BYTES[6] = '{1, 4, 42, 64, 129, 512}; - synth_axi_lite_regs #( - .REG_NUM_BYTES ( NUM_BYTES[i] ), - .AXI_ADDR_WIDTH ( 32'd32 ), - .AXI_DATA_WIDTH ( 32'd32 ) - ) i_axi_lite_regs (.*); - end - -endmodule - - -module synth_slice #( - parameter int AW = -1, - parameter int DW = -1, - parameter int IW = -1, - parameter int UW = -1 -)( - input logic clk_i, - input logic rst_ni -); - - AXI_BUS #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW), - .AXI_ID_WIDTH(IW), - .AXI_USER_WIDTH(UW) - ) a_full(), b_full(); - - AXI_LITE #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW) - ) a_lite(), b_lite(); - - axi_to_axi_lite_intf #( - .AXI_ID_WIDTH (IW), - .AXI_ADDR_WIDTH (AW), - .AXI_DATA_WIDTH (DW), - .AXI_USER_WIDTH (UW), - .AXI_MAX_WRITE_TXNS (32'd10), - .AXI_MAX_READ_TXNS (32'd10), - .FALL_THROUGH (1'b0) - ) a ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .testmode_i (1'b0), - .slv (a_full.Slave), - .mst (a_lite.Master) - ); - axi_lite_to_axi_intf #( - .AXI_DATA_WIDTH (DW) - ) b ( - .in (b_lite.Slave), - .slv_aw_cache_i ('0), - .slv_ar_cache_i ('0), - .out (b_full.Master) - ); - -endmodule - - -module synth_axi_atop_filter #( - parameter int unsigned AXI_ADDR_WIDTH = 0, - parameter int unsigned AXI_DATA_WIDTH = 0, - parameter int unsigned AXI_ID_WIDTH = 0, - parameter int unsigned AXI_USER_WIDTH = 0, - parameter int unsigned AXI_MAX_WRITE_TXNS = 0 -) ( - input logic clk_i, - input logic rst_ni -); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) upstream (); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) downstream (); - - axi_atop_filter_intf #( - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_MAX_WRITE_TXNS (AXI_MAX_WRITE_TXNS) - ) dut ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .slv (upstream), - .mst (downstream) - ); -endmodule - -`include "axi/typedef.svh" - -module synth_axi_lite_to_apb #( - parameter int unsigned NoApbSlaves = 0, - parameter int unsigned DataWidth = 0 -) ( - input logic clk_i, // Clock - input logic rst_ni // Asynchronous reset active low -); - - typedef logic [31:0] addr_t; - typedef logic [DataWidth-1:0] data_t; - typedef logic [DataWidth/8-1:0] strb_t; - - typedef struct packed { - addr_t paddr; // same as AXI4-Lite - axi_pkg::prot_t pprot; // same as AXI4-Lite, specification is the same - logic psel; // one request line per connected APB4 slave - logic penable; // enable signal shows second APB4 cycle - logic pwrite; // write enable - data_t pwdata; // write data, comes from W channel - strb_t pstrb; // write strb, comes from W channel - } apb_req_t; - - typedef struct packed { - logic pready; // slave signals that it is ready - data_t prdata; // read data, connects to R channel - logic pslverr; // gets translated into either `axi_pkg::RESP_OK` or `axi_pkg::RESP_SLVERR` - } apb_resp_t; - - `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) - `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) - `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) - `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) - `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - - axi_req_t axi_req; - axi_resp_t axi_resp; - apb_req_t [NoApbSlaves-1:0] apb_req; - apb_resp_t [NoApbSlaves-1:0] apb_resp; - - axi_pkg::xbar_rule_32_t [NoApbSlaves-1:0] addr_map; - - axi_lite_to_apb #( - .NoApbSlaves ( NoApbSlaves ), - .NoRules ( NoApbSlaves ), - .AddrWidth ( 32'd32 ), - .DataWidth ( DataWidth ), - .axi_lite_req_t ( axi_req_t ), - .axi_lite_resp_t ( axi_resp_t ), - .apb_req_t ( apb_req_t ), - .apb_resp_t ( apb_resp_t ), - .rule_t ( axi_pkg::xbar_rule_32_t ) - ) i_axi_lite_to_apb_dut ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .axi_lite_req_i ( axi_req ), - .axi_lite_resp_o ( axi_resp ), - .apb_req_o ( apb_req ), - .apb_resp_i ( apb_resp ), - .addr_map_i ( addr_map ) - ); - -endmodule - -module synth_axi_cdc #( - parameter int unsigned AXI_ADDR_WIDTH = 0, - parameter int unsigned AXI_DATA_WIDTH = 0, - parameter int unsigned AXI_ID_WIDTH = 0, - parameter int unsigned AXI_USER_WIDTH = 0 -) ( - input logic clk_i, - input logic rst_ni -); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) upstream (); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) downstream (); - - axi_cdc_intf #( - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH), - .LOG_DEPTH (2) - ) dut ( - .src_clk_i (clk_i), - .src_rst_ni (rst_ni), - .src (upstream), - .dst_clk_i (clk_i), - .dst_rst_ni (rst_ni), - .dst (downstream) - ); - -endmodule - -`include "axi/typedef.svh" - -module synth_axi_lite_xbar #( - parameter int unsigned NoSlvMst = 32'd1 -) ( - input logic clk_i, // Clock - input logic rst_ni // Asynchronous reset active low -); - typedef logic [32'd32-1:0] addr_t; - typedef logic [32'd32-1:0] data_t; - typedef logic [32'd32/8-1:0] strb_t; - - `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) - `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) - `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) - `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) - `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) - localparam axi_pkg::xbar_cfg_t XbarCfg = '{ - NoSlvPorts: NoSlvMst, - NoMstPorts: NoSlvMst, - MaxMstTrans: 32'd5, - MaxSlvTrans: 32'd5, - FallThrough: 1'b1, - LatencyMode: axi_pkg::CUT_ALL_PORTS, - AxiAddrWidth: 32'd32, - AxiDataWidth: 32'd32, - NoAddrRules: NoSlvMst, - default: '0 - }; - - axi_pkg::xbar_rule_32_t [NoSlvMst-1:0] addr_map; - logic test; - req_t [NoSlvMst-1:0] mst_reqs, slv_reqs; - resp_t [NoSlvMst-1:0] mst_resps, slv_resps; - - axi_lite_xbar #( - .Cfg ( XbarCfg ), - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ), - .rule_t ( axi_pkg::xbar_rule_32_t ) - ) i_xbar_dut ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .test_i ( test ), - .slv_ports_req_i ( mst_reqs ), - .slv_ports_resp_o ( mst_resps ), - .mst_ports_req_o ( slv_reqs ), - .mst_ports_resp_i ( slv_resps ), - .addr_map_i ( addr_map ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) - ); -endmodule - -module synth_axi_lite_mailbox #( - parameter int unsigned MAILBOX_DEPTH = 32'd1, - parameter bit IRQ_EDGE_TRIG = 1'b0, - parameter bit IRQ_ACT_HIGH = 1'b0 -) ( - input logic clk_i, // Clock - input logic rst_ni // Asynchronous reset active low -); - typedef logic [32'd32-1:0] addr_t; - - AXI_LITE #( - .AXI_ADDR_WIDTH (32'd32), - .AXI_DATA_WIDTH (32'd32) - ) slv [1:0] (); - - logic test; - logic [1:0] irq; - addr_t [1:0] base_addr; - - axi_lite_mailbox_intf #( - .MAILBOX_DEPTH ( MAILBOX_DEPTH ), - .IRQ_EDGE_TRIG ( IRQ_EDGE_TRIG ), - .IRQ_ACT_HIGH ( IRQ_ACT_HIGH ), - .AXI_ADDR_WIDTH ( 32'd32 ), - .AXI_DATA_WIDTH ( 32'd32 ) - ) i_axi_lite_mailbox ( - .clk_i ( clk_i ), // Clock - .rst_ni ( rst_ni ), // Asynchronous reset active low - .test_i ( test ), // Testmode enable - // slave ports [1:0] - .slv ( slv ), - .irq_o ( irq ), // interrupt output for each port - .base_addr_i ( base_addr ) // base address for each port - ); -endmodule - -module synth_axi_isolate #( - parameter int unsigned NumPending = 32'd16, // number of pending requests - parameter int unsigned AxiIdWidth = 32'd0, // AXI ID width - parameter int unsigned AxiAddrWidth = 32'd0, // AXI address width - parameter int unsigned AxiDataWidth = 32'd0, // AXI data width - parameter int unsigned AxiUserWidth = 32'd0 // AXI user width -) ( - input clk_i, - input rst_ni -); - - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiIdWidth ), - .AXI_DATA_WIDTH ( AxiAddrWidth ), - .AXI_ID_WIDTH ( AxiDataWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) axi[1:0] (); - - logic isolate, isolated; - - axi_isolate_intf #( - .NUM_PENDING ( NumPending ), // number of pending requests - .AXI_ID_WIDTH ( AxiIdWidth ), // AXI ID width - .AXI_ADDR_WIDTH ( AxiAddrWidth ), // AXI address width - .AXI_DATA_WIDTH ( AxiDataWidth ), // AXI data width - .AXI_USER_WIDTH ( AxiUserWidth ) // AXI user width - ) i_axi_isolate_dut ( - .clk_i, - .rst_ni, - .slv ( axi[0] ), // slave port - .mst ( axi[1] ), // master port - .isolate_i ( isolate ), // isolate master port from slave port - .isolated_o ( isolated ) // master port is isolated from slave port - ); -endmodule - -module synth_axi_modify_address #( - parameter int unsigned AXI_SLV_PORT_ADDR_WIDTH = 0, - parameter int unsigned AXI_MST_PORT_ADDR_WIDTH = 0, - parameter int unsigned AXI_DATA_WIDTH = 0, - parameter int unsigned AXI_ID_WIDTH = 0, - parameter int unsigned AXI_USER_WIDTH = 0 -) (); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_SLV_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) upstream (); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_MST_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) downstream (); - - logic [AXI_MST_PORT_ADDR_WIDTH-1:0] mst_aw_addr, - mst_ar_addr; - axi_modify_address_intf #( - .AXI_SLV_PORT_ADDR_WIDTH (AXI_SLV_PORT_ADDR_WIDTH), - .AXI_MST_PORT_ADDR_WIDTH (AXI_MST_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) dut ( - .slv (upstream), - .mst_aw_addr_i (mst_aw_addr), - .mst_ar_addr_i (mst_ar_addr), - .mst (downstream) - ); -endmodule - -module synth_axi_serializer #( - parameter int unsigned NumPending = 32'd16, // number of pending requests - parameter int unsigned AxiIdWidth = 32'd0, // AXI ID width - parameter int unsigned AxiAddrWidth = 32'd0, // AXI address width - parameter int unsigned AxiDataWidth = 32'd0, // AXI data width - parameter int unsigned AxiUserWidth = 32'd0 // AXI user width -) ( - input clk_i, - input rst_ni -); - - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiIdWidth ), - .AXI_DATA_WIDTH ( AxiAddrWidth ), - .AXI_ID_WIDTH ( AxiDataWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) axi[1:0] (); - - axi_serializer_intf #( - .MAX_READ_TXNS ( NumPending ), // Number of pending requests - .MAX_WRITE_TXNS ( NumPending ), // Number of pending requests - .AXI_ID_WIDTH ( AxiIdWidth ), // AXI ID width - .AXI_ADDR_WIDTH ( AxiAddrWidth ), // AXI address width - .AXI_DATA_WIDTH ( AxiDataWidth ), // AXI data width - .AXI_USER_WIDTH ( AxiUserWidth ) // AXI user width - ) i_axi_isolate_dut ( - .clk_i, - .rst_ni, - .slv ( axi[0] ), // slave port - .mst ( axi[1] ) // master port - ); -endmodule - -module synth_axi_lite_regs #( - parameter int unsigned REG_NUM_BYTES = 32'd0, - parameter int unsigned AXI_ADDR_WIDTH = 32'd0, - parameter int unsigned AXI_DATA_WIDTH = 32'd0 -) ( - input logic clk_i, - input logic rst_ni -); - typedef logic [7:0] byte_t; - - AXI_LITE #( - .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ) - ) slv (); - - logic [REG_NUM_BYTES-1:0] wr_active, rd_active; - byte_t [REG_NUM_BYTES-1:0] reg_d, reg_q; - logic [REG_NUM_BYTES-1:0] reg_load; - - axi_lite_regs_intf #( - .REG_NUM_BYTES ( REG_NUM_BYTES ), - .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .PRIV_PROT_ONLY ( 1'd0 ), - .SECU_PROT_ONLY ( 1'd0 ), - .AXI_READ_ONLY ( {REG_NUM_BYTES{1'b0}} ), - .REG_RST_VAL ( {REG_NUM_BYTES{8'h00}} ) - ) i_axi_lite_regs ( - .clk_i, - .rst_ni, - .slv ( slv ), - .wr_active_o ( wr_active ), - .rd_active_o ( rd_active ), - .reg_d_i ( reg_d ), - .reg_load_i ( reg_load ), - .reg_q_o ( reg_q ) - ); -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_addr_test.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_addr_test.sv deleted file mode 100644 index 49d8c3f1a..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_addr_test.sv +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger -// - Andreas Kurth - -// Description: Simple test for showing address wrapping behavior of the function -// `axi_pkg::beat_addr` - -`include "axi/assign.svh" -/// Test bench for address generation -module tb_axi_addr_test #( - /// Number of calculated AX transfers - int unsigned NumTests = 32'd10000, - /// Print each calculated address - bit PrintDbg = 1'b0 -); - - localparam int unsigned AxiIdWidth = 32'd1; - localparam int unsigned AxiAddrWidth = 32'd16; - localparam int unsigned AxiDataWidth = 32'd1024; - localparam int unsigned AxiUserWidth = 32'd1; - - // Sim print config, how many transactions - localparam int unsigned PrintTnx = 1000; - localparam int unsigned NoReads = 0; - localparam int unsigned NoWrites = NumTests; - - - typedef logic [AxiAddrWidth:0] addr_t; - - /// The data transferred on a beat on the AW/AR channels. - class ax_transfer; - rand addr_t addr = '0; - rand axi_pkg::len_t len = '0; - rand axi_pkg::size_t size = '0; - rand axi_pkg::burst_t burst = '0; - endclass - - // Random master no Transactions - localparam int unsigned NoPendingDut = 16; - - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - - typedef axi_test::axi_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidth ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - // Enable burst types - .AXI_BURST_FIXED ( 1'b1 ), - .AXI_BURST_INCR ( 1'b1 ), - .AXI_BURST_WRAP ( 1'b1 ) - ) axi_rand_master_t; - typedef axi_test::axi_rand_slave #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidth ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) - ) axi_rand_slave_t; - // ------------- - // DUT signals - // ------------- - logic clk; - logic rst_n; - logic end_of_sim; - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv (clk); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv (clk); - - `AXI_ASSIGN(slave_dv, master_dv) - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - initial begin : proc_axi_master - automatic axi_rand_master_t axi_rand_master = new(master_dv); - end_of_sim <= 1'b0; - axi_rand_master.add_memory_region(16'h0000, 16'hFFFF, axi_pkg::DEVICE_NONBUFFERABLE); - axi_rand_master.add_memory_region(16'h0000, 16'hFFFF, axi_pkg::WTHRU_NOALLOCATE); - axi_rand_master.add_memory_region(16'h0000, 16'hFFFF, axi_pkg::WBACK_RWALLOCATE); - axi_rand_master.reset(); - @(posedge rst_n); - axi_rand_master.run(0, NumTests); - end_of_sim <= 1'b1; - repeat (10000) @(posedge clk); - $stop(); - end - - initial begin : proc_axi_slave - automatic axi_rand_slave_t axi_rand_slave = new(slave_dv); - axi_rand_slave.reset(); - @(posedge rst_n); - axi_rand_slave.run(); - end - - initial begin : proc_sim_progress - automatic int unsigned aw = 0; - automatic int unsigned ar = 0; - automatic bit aw_printed = 1'b0; - automatic bit ar_printed = 1'b0; - - @(posedge rst_n); - - forever begin - @(posedge clk); - #TestTime; - if (master_dv.aw_valid && master_dv.aw_ready) begin - aw++; - end - if (master_dv.ar_valid && master_dv.ar_ready) begin - ar++; - end - - if ((aw % PrintTnx == 0) && ! aw_printed) begin - $display("%t> Transmit AW %d of %d.", $time(), aw, NoWrites); - aw_printed = 1'b1; - end - if ((ar % PrintTnx == 0) && !ar_printed) begin - $display("%t> Transmit AR %d of %d.", $time(), ar, NoReads); - ar_printed = 1'b1; - end - - if (aw % PrintTnx == 1) begin - aw_printed = 1'b0; - end - if (ar % PrintTnx == 1) begin - ar_printed = 1'b0; - end - - if (end_of_sim) begin - $info("All transactions completed."); - break; - end - end - end - - // Test Address queue - ax_transfer ax_queue[$]; - addr_t gold_addr[$]; - - initial begin : generate_tests - automatic logic rand_success; - automatic ax_transfer ax_beat; - - forever begin - @(posedge clk); - #TestTime; - if (master_dv.aw_valid && master_dv.aw_ready) begin - ax_beat = new; - ax_beat.addr = master_dv.aw_addr; - ax_beat.len = master_dv.aw_len; - ax_beat.size = master_dv.aw_size; - ax_beat.burst = master_dv.aw_burst; - - ax_queue.push_back(ax_beat); - end - end - end - - initial begin : proc_test - automatic ax_transfer ax_beat; - automatic addr_t test_addr, exp_addr; - for (int unsigned i = 0; i < NumTests; i++) begin - wait (ax_queue.size()); - // get current transfer - ax_beat = ax_queue.pop_front(); - if (PrintDbg) begin - print_ax(ax_beat); - end - // golden model derived from pseudocode from A-52 - data_transfer(ax_beat.addr, (2**ax_beat.size), (ax_beat.len+1), ax_beat.burst); - // test the calculated addresses - for (int unsigned i = 0; i <= ax_beat.len; i++) begin - test_addr = axi_pkg::beat_addr(ax_beat.addr, ax_beat.size, ax_beat.len, ax_beat.burst, i); - exp_addr = gold_addr.pop_front(); - if (PrintDbg) begin - print_addr(test_addr, i); - end - assert (test_addr == exp_addr) else - begin - print_ax(ax_beat); - print_addr(test_addr, i); - $error("Expected ADDR: %0h was ADDR: %0h", exp_addr, test_addr); - end - end - end - end - - // golden model derived from pseudocode from A-52 - function automatic void data_transfer(addr_t start_addr, int unsigned num_bytes, - int unsigned burst_length, axi_pkg::burst_t mode); - // define boundaries wider than the address, to finf wrapp of addr space - localparam int unsigned large_addr = $bits(addr_t); - typedef logic [large_addr:0] laddr_t; - - laddr_t addr; - laddr_t aligned_addr; - bit aligned; - int unsigned dtsize; - laddr_t lower_wrap_boundary; - laddr_t upper_wrap_boundary; - assume (mode inside {axi_pkg::BURST_FIXED, axi_pkg::BURST_INCR, axi_pkg::BURST_WRAP}); - addr = laddr_t'(start_addr); - aligned_addr = laddr_t'((addr / num_bytes) * num_bytes); - aligned = (aligned_addr == addr); - dtsize = num_bytes * burst_length; - - if (mode == axi_pkg::BURST_WRAP) begin - lower_wrap_boundary = laddr_t'((addr / dtsize) * dtsize); - upper_wrap_boundary = lower_wrap_boundary + dtsize; - end - - for (int unsigned n = 1; n <= burst_length; n++) begin - gold_addr.push_back(addr_t'(addr)); - // increment address if necessary - if (mode != axi_pkg::BURST_FIXED) begin - if (aligned) begin - addr += num_bytes; - end else begin - addr = aligned_addr + num_bytes; - aligned = 1'b1; - end - if (mode == axi_pkg::BURST_WRAP && (addr >= upper_wrap_boundary)) begin - addr = lower_wrap_boundary; - end - end - end - endfunction : data_transfer - - function automatic void print_ax (ax_transfer ax); - $display("####################################################################",); - $display("AX transfer with:"); - case (ax.burst) - axi_pkg::BURST_FIXED: $display("TYPE: BURST_FIXED"); - axi_pkg::BURST_INCR: $display("TYPE: BURST_INCR"); - axi_pkg::BURST_WRAP: $display("TYPE: BURST_WRAP"); - default : $error("TYPE: NOT_DEFINED"); - endcase - $display("ADDR: %0h", ax.addr); - $display("SIZE: %0h", ax.size); - $display("LEN: %0h", ax.len); - endfunction : print_ax - - function automatic void print_addr(addr_t addr, int unsigned i_addr); - $display("i_beat: %0h ADDR: %0h", i_addr, addr); - endfunction : print_addr - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_atop_filter.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_atop_filter.sv deleted file mode 100644 index 1f5d89a6b..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_atop_filter.sv +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Andreas Kurth - -// Testbench for axi_atop_filter - -`include "axi/assign.svh" - -module tb_axi_atop_filter #( - // AXI Parameters - parameter int unsigned TB_AXI_ADDR_WIDTH = 32, - parameter int unsigned TB_AXI_DATA_WIDTH = 64, - parameter int unsigned TB_AXI_ID_WIDTH = 4, - parameter int unsigned TB_AXI_USER_WIDTH = 2, - parameter int unsigned TB_AXI_MAX_READ_TXNS = 10, - parameter int unsigned TB_AXI_MAX_WRITE_TXNS = 12, - // TB Parameters - parameter time TB_TCLK = 10ns, - parameter time TB_TA = TB_TCLK * 1/4, - parameter time TB_TT = TB_TCLK * 3/4, - parameter int unsigned TB_REQ_MIN_WAIT_CYCLES = 0, - parameter int unsigned TB_REQ_MAX_WAIT_CYCLES = 10, - parameter int unsigned TB_RESP_MIN_WAIT_CYCLES = 0, - parameter int unsigned TB_RESP_MAX_WAIT_CYCLES = TB_REQ_MAX_WAIT_CYCLES/2, - parameter int unsigned TB_N_TXNS = 1000 -); - - import axi_pkg::ATOP_ATOMICCMP; - import axi_pkg::ATOP_ATOMICLOAD; - import axi_pkg::ATOP_ATOMICSTORE; - import axi_pkg::BURST_FIXED; - import axi_pkg::BURST_INCR; - import axi_pkg::BURST_WRAP; - import axi_pkg::RESP_OKAY; - import axi_pkg::RESP_SLVERR; - import rand_id_queue_pkg::rand_id_queue; - import rand_verif_pkg::rand_wait; - - localparam int unsigned AXI_STRB_WIDTH = TB_AXI_DATA_WIDTH / 8; - localparam int unsigned NUM_AXI_IDS = 2**TB_AXI_ID_WIDTH; - - logic clk, - rst_n; - - clk_rst_gen #( - .ClkPeriod (TB_TCLK), - .RstClkCycles (5) - ) i_clk_rst_gen ( - .clk_o (clk), - .rst_no (rst_n) - ); - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (TB_AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (TB_AXI_DATA_WIDTH), - .AXI_ID_WIDTH (TB_AXI_ID_WIDTH), - .AXI_USER_WIDTH (TB_AXI_USER_WIDTH) - ) upstream_dv ( - .clk_i (clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH (TB_AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (TB_AXI_DATA_WIDTH), - .AXI_ID_WIDTH (TB_AXI_ID_WIDTH), - .AXI_USER_WIDTH (TB_AXI_USER_WIDTH) - ) upstream (); - - `AXI_ASSIGN(upstream, upstream_dv) - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (TB_AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (TB_AXI_DATA_WIDTH), - .AXI_ID_WIDTH (TB_AXI_ID_WIDTH), - .AXI_USER_WIDTH (TB_AXI_USER_WIDTH) - ) downstream_dv ( - .clk_i (clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH (TB_AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (TB_AXI_DATA_WIDTH), - .AXI_ID_WIDTH (TB_AXI_ID_WIDTH), - .AXI_USER_WIDTH (TB_AXI_USER_WIDTH) - ) downstream (); - - `AXI_ASSIGN(downstream_dv, downstream) - - axi_atop_filter_intf #( - .AXI_ID_WIDTH (TB_AXI_ID_WIDTH), - .AXI_MAX_WRITE_TXNS (TB_AXI_MAX_WRITE_TXNS), - .AXI_ADDR_WIDTH (TB_AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH (TB_AXI_DATA_WIDTH), - .AXI_USER_WIDTH (TB_AXI_USER_WIDTH) - ) dut ( - .clk_i (clk), - .rst_ni (rst_n), - .slv (upstream), - .mst (downstream) - ); - - typedef logic [TB_AXI_ID_WIDTH-1:0] axi_id_t; - - // AXI Master - logic mst_done = 1'b0; - axi_test::axi_rand_master #( - .AW(TB_AXI_ADDR_WIDTH), .DW(TB_AXI_DATA_WIDTH), .IW(TB_AXI_ID_WIDTH), .UW(TB_AXI_USER_WIDTH), - .TA(TB_TA), .TT(TB_TT), - .MAX_READ_TXNS (TB_AXI_MAX_READ_TXNS), - .MAX_WRITE_TXNS (TB_AXI_MAX_WRITE_TXNS+2), // master is not required to comply - .AX_MIN_WAIT_CYCLES (TB_REQ_MIN_WAIT_CYCLES), - .AX_MAX_WAIT_CYCLES (TB_REQ_MAX_WAIT_CYCLES), - .W_MIN_WAIT_CYCLES (TB_REQ_MIN_WAIT_CYCLES), - .W_MAX_WAIT_CYCLES (TB_REQ_MAX_WAIT_CYCLES), - .RESP_MIN_WAIT_CYCLES (TB_RESP_MIN_WAIT_CYCLES), - .RESP_MAX_WAIT_CYCLES (TB_RESP_MAX_WAIT_CYCLES), - .AXI_ATOPS (1'b1) - ) axi_master = new(upstream_dv); - initial begin - axi_master.reset(); - wait(rst_n); - axi_master.add_memory_region({TB_AXI_ADDR_WIDTH{1'b0}}, {TB_AXI_ADDR_WIDTH{1'b1}}, axi_pkg::WTHRU_NOALLOCATE); - axi_master.run(TB_N_TXNS, TB_N_TXNS); - mst_done = 1'b1; - end - - initial begin - wait (mst_done); - $finish(); - end - - // AXI Slave - axi_test::axi_rand_slave #( - .AW(TB_AXI_ADDR_WIDTH), .DW(TB_AXI_DATA_WIDTH), .IW(TB_AXI_ID_WIDTH), .UW(TB_AXI_USER_WIDTH), - .TA(TB_TA), .TT(TB_TT), - .AX_MIN_WAIT_CYCLES (TB_RESP_MIN_WAIT_CYCLES), - .AX_MAX_WAIT_CYCLES (TB_RESP_MAX_WAIT_CYCLES), - .R_MIN_WAIT_CYCLES (TB_RESP_MIN_WAIT_CYCLES), - .R_MAX_WAIT_CYCLES (TB_RESP_MAX_WAIT_CYCLES), - .RESP_MIN_WAIT_CYCLES (TB_RESP_MIN_WAIT_CYCLES), - .RESP_MAX_WAIT_CYCLES (TB_RESP_MAX_WAIT_CYCLES) - ) axi_slave = new(downstream_dv); - initial begin - axi_slave.reset(); - wait (rst_n); - axi_slave.run(); - end - - typedef struct packed { - axi_id_t id; - logic thru; - } w_cmd_t; - - typedef axi_test::axi_ax_beat #( - .AW(TB_AXI_ADDR_WIDTH), .IW(TB_AXI_ID_WIDTH), .UW(TB_AXI_USER_WIDTH) - ) ax_beat_t; - typedef axi_test::axi_b_beat #( - .IW(TB_AXI_ID_WIDTH), .UW(TB_AXI_USER_WIDTH) - ) b_beat_t; - typedef axi_test::axi_r_beat #( - .DW(TB_AXI_DATA_WIDTH), .IW(TB_AXI_ID_WIDTH), .UW(TB_AXI_USER_WIDTH) - ) r_beat_t; - typedef axi_test::axi_w_beat #( - .DW(TB_AXI_DATA_WIDTH), .UW(TB_AXI_USER_WIDTH) - ) w_beat_t; - - // Put W beats into transfer queue or drop them and inject B responses based on W command. - function automatic void process_w_beat(w_beat_t w_beat, ref w_cmd_t w_cmd_queue[$], - ref w_beat_t w_xfer_queue[$], ref b_beat_t b_inject_queue[$] - ); - w_cmd_t w_cmd = w_cmd_queue[0]; - if (w_cmd.thru) begin - w_xfer_queue.push_back(w_beat); - end - if (w_beat.w_last) begin - if (!w_cmd.thru) begin - automatic b_beat_t b_beat = new; - b_beat.b_id = w_cmd.id; - b_beat.b_resp = RESP_SLVERR; - b_inject_queue.push_back(b_beat); - end - void'(w_cmd_queue.pop_front()); - end - endfunction - - // Monitor and check responses of filter. - initial begin - static ax_beat_t ar_xfer_queue[$], - aw_xfer_queue[$]; - static b_beat_t b_inject_queue[$], - b_xfer_queue[$]; - static r_beat_t r_inject_queue[$], - r_xfer_queue[$]; - static w_cmd_t w_cmd_queue[$]; - static w_beat_t w_act_queue[$], - w_undecided_queue[$], - w_xfer_queue[$]; - forever begin - @(posedge clk); - #(TB_TT); - // Ensure that downstream never sees an `aw_atop`. - if (downstream.aw_valid) begin - assert (downstream.aw_atop == '0); - end - // Push upstream ARs into transfer queues. - if (upstream.ar_valid && upstream.ar_ready) begin - automatic ax_beat_t ar_beat = new; - ar_beat.ax_id = upstream.ar_id; - ar_beat.ax_addr = upstream.ar_addr; - ar_beat.ax_len = upstream.ar_len; - ar_beat.ax_size = upstream.ar_size; - ar_beat.ax_burst = upstream.ar_burst; - ar_beat.ax_lock = upstream.ar_lock; - ar_beat.ax_cache = upstream.ar_cache; - ar_beat.ax_prot = upstream.ar_prot; - ar_beat.ax_qos = upstream.ar_qos; - ar_beat.ax_region = upstream.ar_region; - ar_beat.ax_user = upstream.ar_user; - ar_xfer_queue.push_back(ar_beat); - end - // Push upstream AWs that must go through into transfer queues, and push to W command queue. - if (upstream.aw_valid && upstream.aw_ready) begin - automatic ax_beat_t aw_beat = new; - automatic w_cmd_t w_cmd; - aw_beat.ax_id = upstream.aw_id; - aw_beat.ax_addr = upstream.aw_addr; - aw_beat.ax_len = upstream.aw_len; - aw_beat.ax_size = upstream.aw_size; - aw_beat.ax_burst = upstream.aw_burst; - aw_beat.ax_lock = upstream.aw_lock; - aw_beat.ax_cache = upstream.aw_cache; - aw_beat.ax_prot = upstream.aw_prot; - aw_beat.ax_qos = upstream.aw_qos; - aw_beat.ax_region = upstream.aw_region; - aw_beat.ax_atop = upstream.aw_atop; - aw_beat.ax_user = upstream.aw_user; - w_cmd.id = aw_beat.ax_id; - w_cmd.thru = (aw_beat.ax_atop == '0); - w_cmd_queue.push_back(w_cmd); - if (w_cmd.thru) begin - aw_xfer_queue.push_back(aw_beat); - end else if (aw_beat.ax_atop[5:4] != ATOP_ATOMICSTORE) begin - for (int unsigned i = 0; i < aw_beat.ax_len + 1; i++) begin - automatic r_beat_t r_beat = new; - r_beat.r_id = aw_beat.ax_id; - r_beat.r_resp = RESP_SLVERR; - r_beat.r_data = '0; - r_beat.r_user = '0; - r_beat.r_last = (i == aw_beat.ax_len); - r_inject_queue.push_back(r_beat); - end - end - end - // Handle undecided upstream W beats if possible. - while (w_undecided_queue.size() > 0 && w_cmd_queue.size() > 0) begin - automatic w_beat_t w_beat = w_undecided_queue.pop_front(); - process_w_beat(w_beat, w_cmd_queue, w_xfer_queue, b_inject_queue); - end - // Process upstream W beats or put them into queue of undecided W beats. - if (upstream.w_valid && upstream.w_ready) begin - automatic w_beat_t w_beat = new; - w_beat.w_data = upstream.w_data; - w_beat.w_strb = upstream.w_strb; - w_beat.w_last = upstream.w_last; - w_beat.w_user = upstream.w_user; - if (w_cmd_queue.size() > 0) begin - process_w_beat(w_beat, w_cmd_queue, w_xfer_queue, b_inject_queue); - end else begin - w_undecided_queue.push_back(w_beat); - end - end - // Push downstream Rs into transfer queue. - if (downstream.r_valid && downstream.r_ready) begin - automatic r_beat_t r_beat = new; - r_beat.r_id = downstream.r_id; - r_beat.r_data = downstream.r_data; - r_beat.r_resp = downstream.r_resp; - r_beat.r_last = downstream.r_last; - r_beat.r_user = downstream.r_user; - r_xfer_queue.push_back(r_beat); - end - // Push downstream Bs into transfer queue. - if (downstream.b_valid && downstream.b_ready) begin - automatic b_beat_t b_beat = new; - b_beat.b_id = downstream.b_id; - b_beat.b_resp = downstream.b_resp; - b_beat.b_user = downstream.b_user; - b_xfer_queue.push_back(b_beat); - end - // Ensure downstream ARs match beats from transfer queue. - if (downstream.ar_valid && downstream.ar_ready) begin - automatic ax_beat_t exp_beat; - assert (ar_xfer_queue.size() > 0) else $fatal(1, "downstream.AR: Unknown beat!"); - exp_beat = ar_xfer_queue.pop_front(); - assert (downstream.ar_id == exp_beat.ax_id); - assert (downstream.ar_addr == exp_beat.ax_addr); - assert (downstream.ar_len == exp_beat.ax_len); - assert (downstream.ar_size == exp_beat.ax_size); - assert (downstream.ar_burst == exp_beat.ax_burst); - assert (downstream.ar_cache == exp_beat.ax_cache); - assert (downstream.ar_prot == exp_beat.ax_prot); - assert (downstream.ar_qos == exp_beat.ax_qos); - assert (downstream.ar_region == exp_beat.ax_region); - assert (downstream.ar_user == exp_beat.ax_user); - end - // Ensure downstream AWs match beats from transfer queue. - if (downstream.aw_valid && downstream.aw_ready) begin - automatic ax_beat_t exp_beat; - assert (aw_xfer_queue.size() > 0) else $fatal(1, "downstream.AW: Unknown beat!"); - exp_beat = aw_xfer_queue.pop_front(); - assert (downstream.aw_id == exp_beat.ax_id); - assert (downstream.aw_addr == exp_beat.ax_addr); - assert (downstream.aw_len == exp_beat.ax_len); - assert (downstream.aw_size == exp_beat.ax_size); - assert (downstream.aw_burst == exp_beat.ax_burst); - assert (downstream.aw_cache == exp_beat.ax_cache); - assert (downstream.aw_prot == exp_beat.ax_prot); - assert (downstream.aw_qos == exp_beat.ax_qos); - assert (downstream.aw_region == exp_beat.ax_region); - assert (downstream.aw_user == exp_beat.ax_user); - end - // Ensure downstream Ws match beats from transfer queue. - while (w_act_queue.size() > 0 && w_xfer_queue.size() > 0) begin - automatic w_beat_t exp_beat = w_xfer_queue.pop_front(); - automatic w_beat_t act_beat = w_act_queue.pop_front(); - assert (act_beat.w_data == exp_beat.w_data); - assert (act_beat.w_strb == exp_beat.w_strb); - assert (act_beat.w_last == exp_beat.w_last); - assert (act_beat.w_user == exp_beat.w_user); - end - if (downstream.w_valid && downstream.w_ready) begin - if (w_xfer_queue.size() > 0) begin - automatic w_beat_t exp_beat = w_xfer_queue.pop_front(); - assert (downstream.w_data == exp_beat.w_data); - assert (downstream.w_strb == exp_beat.w_strb); - assert (downstream.w_last == exp_beat.w_last); - assert (downstream.w_user == exp_beat.w_user); - end else begin - automatic w_beat_t act_beat = new; - act_beat.w_data = downstream.w_data; - act_beat.w_strb = downstream.w_strb; - act_beat.w_last = downstream.w_last; - act_beat.w_user = downstream.w_user; - w_act_queue.push_back(act_beat); - end - end - // Ensure upstream Rs match beats from transfer or inject queue. - if (upstream.r_valid && upstream.r_ready) begin - automatic r_beat_t exp_beat; - if (r_inject_queue.size() > 0 && r_inject_queue[0].r_id == upstream.r_id) begin - exp_beat = r_inject_queue.pop_front(); - end else if (r_xfer_queue.size() > 0 && r_xfer_queue[0].r_id == upstream.r_id) begin - exp_beat = r_xfer_queue.pop_front(); - end else begin - $fatal(1, "upstream.R: Unknown beat!"); - end - assert (upstream.r_id == exp_beat.r_id); - assert (upstream.r_data == exp_beat.r_data); - assert (upstream.r_resp == exp_beat.r_resp); - assert (upstream.r_last == exp_beat.r_last); - assert (upstream.r_user == exp_beat.r_user); - end - // Ensure upstream Bs match beats from transfer or inject queue. - if (upstream.b_valid && upstream.b_ready) begin - automatic b_beat_t exp_beat; - if (b_inject_queue.size() > 0 && b_inject_queue[0].b_id == upstream.b_id) begin - exp_beat = b_inject_queue.pop_front(); - end else if (b_xfer_queue.size() > 0 && b_xfer_queue[0].b_id == upstream.b_id) begin - exp_beat = b_xfer_queue.pop_front(); - end else begin - $fatal(1, "upstream.B: Unknown beat!"); - end - assert (upstream.b_id == exp_beat.b_id); - assert (upstream.b_resp == exp_beat.b_resp); - assert (upstream.b_user == exp_beat.b_user); - end - end - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_cdc.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_cdc.sv deleted file mode 100644 index 3d51ab9d8..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_cdc.sv +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2019-2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Andreas Kurth -// - Wolfgang Roenninger - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_cdc #( - // AXI Parameters - parameter int unsigned AXI_AW = 32, - parameter int unsigned AXI_DW = 64, - parameter int unsigned AXI_IW = 4, - parameter int unsigned AXI_UW = 2, - parameter int unsigned AXI_MAX_READ_TXNS = 10, - parameter int unsigned AXI_MAX_WRITE_TXNS = 12, - // TB Parameters - parameter time TCLK_UPSTREAM = 10ns, - parameter time TA_UPSTREAM = TCLK_UPSTREAM * 1/4, - parameter time TT_UPSTREAM = TCLK_UPSTREAM * 3/4, - parameter time TCLK_DOWNSTREAM = 3ns, - parameter time TA_DOWNSTREAM = TCLK_DOWNSTREAM * 1/4, - parameter time TT_DOWNSTREAM = TCLK_DOWNSTREAM * 3/4, - parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, - parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, - parameter int unsigned RESP_MIN_WAIT_CYCLES = 0, - parameter int unsigned RESP_MAX_WAIT_CYCLES = REQ_MAX_WAIT_CYCLES/2, - parameter int unsigned N_TXNS = 1000 -); - - localparam int unsigned N_RD_TXNS = N_TXNS / 2; - localparam int unsigned N_WR_TXNS = N_TXNS / 2; - - // Clocks and Resets - - logic upstream_clk, - downstream_clk, - upstream_rst_n, - downstream_rst_n; - - clk_rst_gen #( - .ClkPeriod (TCLK_UPSTREAM), - .RstClkCycles (5) - ) i_clk_rst_gen_upstream ( - .clk_o (upstream_clk), - .rst_no (upstream_rst_n) - ); - - clk_rst_gen #( - .ClkPeriod (TCLK_DOWNSTREAM), - .RstClkCycles (5) - ) i_clk_rst_gen_downstream ( - .clk_o (downstream_clk), - .rst_no (downstream_rst_n) - ); - - // AXI Interfaces - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (AXI_AW), - .AXI_DATA_WIDTH (AXI_DW), - .AXI_ID_WIDTH (AXI_IW), - .AXI_USER_WIDTH (AXI_UW) - ) upstream_dv ( - .clk_i (upstream_clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_AW), - .AXI_DATA_WIDTH (AXI_DW), - .AXI_ID_WIDTH (AXI_IW), - .AXI_USER_WIDTH (AXI_UW) - ) upstream (); - - `AXI_ASSIGN(upstream, upstream_dv) - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (AXI_AW), - .AXI_DATA_WIDTH (AXI_DW), - .AXI_ID_WIDTH (AXI_IW), - .AXI_USER_WIDTH (AXI_UW) - ) downstream_dv ( - .clk_i (downstream_clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_AW), - .AXI_DATA_WIDTH (AXI_DW), - .AXI_ID_WIDTH (AXI_IW), - .AXI_USER_WIDTH (AXI_UW) - ) downstream (); - - `AXI_ASSIGN(downstream_dv, downstream) - - // AXI Channel Structs - - typedef logic [AXI_AW-1:0] addr_t; - typedef logic [AXI_DW-1:0] data_t; - typedef logic [AXI_IW-1:0] id_t; - typedef logic [AXI_DW/8-1:0] strb_t; - typedef logic [AXI_UW-1:0] user_t; - - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) - - axi_cdc_intf #( - .AXI_ADDR_WIDTH (AXI_AW), - .AXI_DATA_WIDTH (AXI_DW), - .AXI_ID_WIDTH (AXI_IW), - .AXI_USER_WIDTH (AXI_UW), - .LOG_DEPTH (2) - ) dut ( - .src_clk_i (upstream_clk), - .src_rst_ni (upstream_rst_n), - .src (upstream), - .dst_clk_i (downstream_clk), - .dst_rst_ni (downstream_rst_n), - .dst (downstream) - ); - - typedef axi_test::axi_rand_master #( - .AW (AXI_AW), - .DW (AXI_DW), - .IW (AXI_IW), - .UW (AXI_UW), - .TA (TA_UPSTREAM), - .TT (TT_UPSTREAM), - .MAX_READ_TXNS (AXI_MAX_READ_TXNS), - .MAX_WRITE_TXNS (AXI_MAX_WRITE_TXNS), - .AX_MIN_WAIT_CYCLES (REQ_MIN_WAIT_CYCLES), - .AX_MAX_WAIT_CYCLES (REQ_MAX_WAIT_CYCLES), - .W_MIN_WAIT_CYCLES (REQ_MIN_WAIT_CYCLES), - .W_MAX_WAIT_CYCLES (REQ_MAX_WAIT_CYCLES), - .RESP_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .RESP_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), - .AXI_MAX_BURST_LEN (16) - ) axi_master_t; - axi_master_t axi_master = new(upstream_dv); - - initial begin - wait (upstream_rst_n); - axi_master.run(N_RD_TXNS, N_WR_TXNS); - end - - typedef axi_test::axi_rand_slave #( - .AW (AXI_AW), - .DW (AXI_DW), - .IW (AXI_IW), - .UW (AXI_UW), - .TA (TA_DOWNSTREAM), - .TT (TT_DOWNSTREAM), - .AX_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .AX_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), - .R_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .R_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), - .RESP_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .RESP_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES) - ) axi_slave_t; - axi_slave_t axi_slave = new(downstream_dv); - - initial begin - wait (downstream_rst_n); - axi_slave.run(); - end - - ar_chan_t mst_ar, slv_ar, ar_queue[$]; - aw_chan_t mst_aw, slv_aw, aw_queue[$]; - b_chan_t mst_b, slv_b, b_queue[$]; - r_chan_t mst_r, slv_r, r_queue[$]; - w_chan_t mst_w, slv_w, w_queue[$]; - - `AXI_ASSIGN_TO_AR(mst_ar, upstream) - `AXI_ASSIGN_TO_AR(slv_ar, downstream) - `AXI_ASSIGN_TO_AW(mst_aw, upstream) - `AXI_ASSIGN_TO_AW(slv_aw, downstream) - `AXI_ASSIGN_TO_B(mst_b, upstream) - `AXI_ASSIGN_TO_B(slv_b, downstream) - `AXI_ASSIGN_TO_R(mst_r, upstream) - `AXI_ASSIGN_TO_R(slv_r, downstream) - `AXI_ASSIGN_TO_W(mst_w, upstream) - `AXI_ASSIGN_TO_W(slv_w, downstream) - - logic mst_done = 1'b0; - // Monitor and check upstream - initial begin - automatic b_chan_t exp_b; - automatic r_chan_t exp_r; - automatic int unsigned rd_cnt = 0, wr_cnt = 0; - forever begin - @(posedge upstream_clk); - #(TT_UPSTREAM); - if (upstream.aw_valid && upstream.aw_ready) begin - aw_queue.push_back(mst_aw); - end - if (upstream.w_valid && upstream.w_ready) begin - w_queue.push_back(mst_w); - end - if (upstream.b_valid && upstream.b_ready) begin - exp_b = b_queue.pop_front(); - assert (mst_b == exp_b); - wr_cnt++; - end - if (upstream.ar_valid && upstream.ar_ready) begin - ar_queue.push_back(mst_ar); - end - if (upstream.r_valid && upstream.r_ready) begin - exp_r = r_queue.pop_front(); - assert (mst_r == exp_r); - if (upstream.r_last) begin - rd_cnt++; - end - end - if (rd_cnt == N_RD_TXNS && wr_cnt == N_WR_TXNS) begin - mst_done = 1'b1; - end - end - end - - // Monitor and check downstream - initial begin - automatic ar_chan_t exp_ar; - automatic aw_chan_t exp_aw; - automatic w_chan_t exp_w; - forever begin - @(posedge downstream_clk); - #(TT_DOWNSTREAM); - if (downstream.aw_valid && downstream.aw_ready) begin - exp_aw = aw_queue.pop_front(); - assert (slv_aw == exp_aw); - end - if (downstream.w_valid && downstream.w_ready) begin - exp_w = w_queue.pop_front(); - assert (slv_w == exp_w); - end - if (downstream.b_valid && downstream.b_ready) begin - b_queue.push_back(slv_b); - end - if (downstream.ar_valid && downstream.ar_ready) begin - exp_ar = ar_queue.pop_front(); - assert (slv_ar == exp_ar); - end - if (downstream.r_valid && downstream.r_ready) begin - r_queue.push_back(slv_r); - end - end - end - - // Terminate simulation after all transactions have completed. - initial begin - wait (mst_done); - #(10*TCLK_UPSTREAM); - $finish(); - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_delayer.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_delayer.sv deleted file mode 100644 index 64b4670da..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_delayer.sv +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Andreas Kurth -// - Florian Zaruba - -`include "axi/assign.svh" - -module tb_axi_delayer; - - parameter AW = 32; - parameter DW = 32; - parameter IW = 8; - parameter UW = 8; - parameter TS = 4; - - localparam tCK = 1ns; - - logic clk = 0; - logic rst = 1; - logic done = 0; - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW), - .AXI_ID_WIDTH(IW), - .AXI_USER_WIDTH(UW) - ) axi_slave_dv(clk), axi_master_dv(clk); - AXI_BUS #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW), - .AXI_ID_WIDTH(IW), - .AXI_USER_WIDTH(UW) - ) axi_slave(), axi_master(); - `AXI_ASSIGN(axi_slave_dv, axi_slave) - `AXI_ASSIGN(axi_master, axi_master_dv) - - axi_delayer_intf #( - .AXI_ADDR_WIDTH ( AW ), - .AXI_DATA_WIDTH ( DW ), - .AXI_ID_WIDTH ( IW ), - .AXI_USER_WIDTH ( UW ), - .FIXED_DELAY_INPUT ( 0 ), - .STALL_RANDOM_INPUT ( 1 ) - ) i_axi_delayer ( - .clk_i ( clk ), - .rst_ni ( rst ), - .slv ( axi_master ), - .mst ( axi_slave ) - ); - - axi_test::axi_driver #(.AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(200ps), .TT(700ps)) axi_slave_drv = new(axi_slave_dv); - axi_test::axi_driver #(.AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(200ps), .TT(700ps)) axi_master_drv = new(axi_master_dv); - - initial begin - #tCK; - rst <= 0; - #tCK; - rst <= 1; - #tCK; - while (!done) begin - clk <= 1; - #(tCK/2); - clk <= 0; - #(tCK/2); - end - end - - initial begin - automatic axi_test::axi_ax_beat #(.AW(AW), .IW(IW), .UW(UW)) ax_beat = new; - automatic axi_test::axi_w_beat #(.DW(DW), .UW(UW)) w_beat = new; - automatic axi_test::axi_b_beat #(.IW(IW), .UW(UW)) b_beat; - automatic logic rand_success; - axi_master_drv.reset_master(); - @(posedge clk); - repeat (200) begin - @(posedge clk); - rand_success = ax_beat.randomize(); assert(rand_success); - axi_master_drv.send_aw(ax_beat); - w_beat.w_data = 'hcafebabe; - axi_master_drv.send_w(w_beat); - end - - repeat (200) axi_master_drv.recv_b(b_beat); - - done = 1; - end - - initial begin - automatic axi_test::axi_ax_beat #(.AW(AW), .IW(IW), .UW(UW)) ax_beat; - automatic axi_test::axi_w_beat #(.DW(DW), .UW(UW)) w_beat; - automatic axi_test::axi_b_beat #(.IW(IW), .UW(UW)) b_beat = new; - automatic int b_id_queue[$]; - axi_slave_drv.reset_slave(); - @(posedge clk); - repeat (200) begin - axi_slave_drv.recv_aw(ax_beat); - $info("AXI AW: addr %h", ax_beat.ax_addr); - axi_slave_drv.recv_w(w_beat); - $info("AXI W: data %h, strb %h", w_beat.w_data, w_beat.w_strb); - b_id_queue.push_back(ax_beat.ax_id); - end - while (b_id_queue.size() != 0) begin - b_beat.b_id = b_id_queue.pop_front(); - axi_slave_drv.send_b(b_beat); - end - end -// vsim -voptargs=+acc work.tb_axi_delayer -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_dw_downsizer.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_dw_downsizer.sv deleted file mode 100644 index ff076ba8c..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_dw_downsizer.sv +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Matheus Cavalcante - -`include "axi/assign.svh" - -module tb_axi_dw_downsizer #( - // AXI Parameters - parameter int unsigned TbAxiAddrWidth = 64 , - parameter int unsigned TbAxiIdWidth = 4 , - parameter int unsigned TbAxiSlvPortDataWidth = 64 , - parameter int unsigned TbAxiMstPortDataWidth = 32 , - parameter int unsigned TbAxiUserWidth = 8 , - // TB Parameters - parameter time TbCyclTime = 10ns, - parameter time TbApplTime = 2ns , - parameter time TbTestTime = 8ns - ); - - /********************* - * CLOCK GENERATOR * - *********************/ - - logic clk; - logic rst_n; - logic eos; - - clk_rst_gen #( - .ClkPeriod (TbCyclTime), - .RstClkCycles (5 ) - ) i_clk_rst_gen ( - .clk_o (clk ), - .rst_no(rst_n) - ); - - /********* - * AXI * - *********/ - - // Master port - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiSlvPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) master_dv ( - .clk_i(clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiSlvPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) master (); - - `AXI_ASSIGN(master, master_dv) - - axi_test::axi_rand_master #( - .AW (TbAxiAddrWidth ), - .DW (TbAxiSlvPortDataWidth), - .IW (TbAxiIdWidth ), - .UW (TbAxiUserWidth ), - .TA (TbApplTime ), - .TT (TbTestTime ), - .MAX_READ_TXNS (8 ), - .MAX_WRITE_TXNS (8 ), - .AXI_BURST_FIXED(1'b0 ), - .AXI_ATOPS (1'b1 ) - ) master_drv = new (master_dv); - - // Slave port - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiMstPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) slave_dv ( - .clk_i(clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiMstPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) slave (); - - axi_test::axi_rand_slave #( - .AW(TbAxiAddrWidth ), - .DW(TbAxiMstPortDataWidth), - .IW(TbAxiIdWidth ), - .UW(TbAxiUserWidth ), - .TA(TbApplTime ), - .TT(TbTestTime ) - ) slave_drv = new (slave_dv); - - `AXI_ASSIGN(slave_dv, slave) - - /********* - * DUT * - *********/ - - axi_dw_converter_intf #( - .AXI_MAX_READS (4 ), - .AXI_ADDR_WIDTH (TbAxiAddrWidth ), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_SLV_PORT_DATA_WIDTH(TbAxiSlvPortDataWidth), - .AXI_MST_PORT_DATA_WIDTH(TbAxiMstPortDataWidth), - .AXI_USER_WIDTH (TbAxiUserWidth ) - ) i_dw_converter ( - .clk_i (clk ), - .rst_ni(rst_n ), - .slv (master), - .mst (slave ) - ); - - /************* - * DRIVERS * - *************/ - - initial begin - eos = 1'b0; - - // Configuration - slave_drv.reset() ; - master_drv.reset() ; - master_drv.add_memory_region({TbAxiAddrWidth{1'b0}}, {TbAxiAddrWidth{1'b1}}, axi_pkg::WTHRU_NOALLOCATE); - - // Wait for the reset before sending requests - @(posedge rst_n); - - fork - // Act as a sink - slave_drv.run() ; - master_drv.run(200, 200); - join_any - - // Done - repeat (10) @(posedge clk); - eos = 1'b1; - end - - /************* - * MONITOR * - *************/ - - initial begin : proc_monitor - static tb_axi_dw_pkg::axi_dw_downsizer_monitor #( - .AxiAddrWidth (TbAxiAddrWidth ), - .AxiMstPortDataWidth(TbAxiMstPortDataWidth), - .AxiSlvPortDataWidth(TbAxiSlvPortDataWidth), - .AxiIdWidth (TbAxiIdWidth ), - .AxiUserWidth (TbAxiUserWidth ), - .TimeTest (TbTestTime ) - ) monitor = new (master_dv, slave_dv); - fork - monitor.run(); - forever begin - #TbTestTime; - if(eos) begin - monitor.print_result(); - $stop() ; - end - @(posedge clk); - end - join - end - -// vsim -voptargs=+acc work.tb_axi_dw_downsizer -endmodule : tb_axi_dw_downsizer diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_dw_pkg.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_dw_pkg.sv deleted file mode 100644 index 9d9bed7fa..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_dw_pkg.sv +++ /dev/null @@ -1,1379 +0,0 @@ -// Copyright (c) 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Matheus Cavalcante - -// `axi_dw_upsizer_monitor` implements an AXI bus monitor that is tuned -// for the AXI Data Width Converters. It snoops on the slave and master port -// of the DWCs and populates FIFOs and ID queues to validate that no -// AXI beats get lost. - -package tb_axi_dw_pkg ; - import axi_pkg::len_t ; - import axi_pkg::burst_t ; - import axi_pkg::size_t ; - import axi_pkg::cache_t ; - import axi_pkg::modifiable; - - /**************** - * BASE CLASS * - ****************/ - - class axi_dw_monitor #( - parameter int unsigned AxiAddrWidth , - parameter int unsigned AxiSlvPortDataWidth, - parameter int unsigned AxiMstPortDataWidth, - parameter int unsigned AxiIdWidth , - parameter int unsigned AxiUserWidth , - // Stimuli application and test time - parameter time TimeTest - ); - - localparam AxiSlvPortStrbWidth = AxiSlvPortDataWidth / 8; - localparam AxiMstPortStrbWidth = AxiMstPortDataWidth / 8; - - localparam AxiSlvPortMaxSize = $clog2(AxiSlvPortStrbWidth); - localparam AxiMstPortMaxSize = $clog2(AxiMstPortStrbWidth); - - typedef logic [AxiIdWidth-1:0] axi_id_t ; - typedef logic [AxiAddrWidth-1:0] axi_addr_t; - - typedef logic [AxiSlvPortDataWidth-1:0] slv_port_data_t; - typedef logic [AxiSlvPortStrbWidth-1:0] slv_port_strb_t; - typedef logic [AxiMstPortDataWidth-1:0] mst_port_data_t; - typedef logic [AxiMstPortStrbWidth-1:0] mst_port_strb_t; - - typedef struct packed { - axi_id_t axi_id; - logic axi_last ; - } exp_b_t; - typedef struct packed { - axi_id_t axi_id ; - slv_port_data_t axi_data; - slv_port_strb_t axi_strb; - logic axi_last ; - } exp_slv_rw_t; - typedef struct packed { - axi_id_t axi_id ; - mst_port_data_t axi_data; - mst_port_strb_t axi_strb; - logic axi_last ; - } exp_mst_rw_t; - typedef struct packed { - axi_id_t axi_id ; - axi_addr_t axi_addr; - len_t axi_len ; - burst_t axi_burst ; - size_t axi_size ; - cache_t axi_cache ; - } exp_ax_t; - - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t (exp_ax_t ), - .ID_WIDTH(AxiIdWidth) - ) ax_queue_t; - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t (exp_b_t ), - .ID_WIDTH(AxiIdWidth) - ) b_queue_t; - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t (exp_slv_rw_t ), - .ID_WIDTH(AxiIdWidth ) - ) slv_r_queue_t; - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t (exp_mst_rw_t ), - .ID_WIDTH(AxiIdWidth ) - ) mst_r_queue_t; - - /********************** - * Helper functions * - **********************/ - - // Returns a byte mask corresponding to the size of the AXI transaction - function automatic axi_addr_t size_mask(axi_pkg::size_t size); - return (axi_addr_t'(1) << size) - 1; - endfunction - - /** - * Returns the conversion rate between tgt_size and src_size. - * @return ceil(num_bytes(tgt_size)/num_bytes(src_size)) - */ - function automatic int unsigned conv_ratio(axi_pkg::size_t tgt_size, axi_pkg::size_t src_size) ; - return (axi_pkg::num_bytes(tgt_size) + axi_pkg::num_bytes(src_size) - 1)/axi_pkg::num_bytes(src_size); - endfunction: conv_ratio - - /************************ - * Virtual Interfaces * - ************************/ - - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiSlvPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) slv_port_axi; - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiMstPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) mst_port_axi; - - /***************** - * Bookkeeping * - *****************/ - - longint unsigned tests_expected; - longint unsigned tests_conducted; - longint unsigned tests_failed; - semaphore cnt_sem; - - // Queues and FIFOs to hold the expected AXIDs - - // Write transactions - ax_queue_t exp_mst_port_aw_queue; - exp_ax_t act_mst_port_aw_queue [$]; - exp_ax_t act_slv_port_aw_queue [$]; - exp_mst_rw_t exp_mst_port_w_queue [$]; - exp_mst_rw_t act_mst_port_w_queue [$]; - exp_slv_rw_t act_slv_port_w_queue [$]; - b_queue_t exp_slv_port_b_queue; - - // Read transactions - ax_queue_t exp_mst_port_ar_queue; - ax_queue_t act_mst_port_ar_queue; - ax_queue_t act_slv_port_ar_queue; - exp_slv_rw_t act_slv_port_r_queue [$]; - slv_r_queue_t exp_slv_port_r_queue; - - /***************** - * Constructor * - *****************/ - - function new ( - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiSlvPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) slv_port_vif, - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiMstPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) mst_port_vif - ); - begin - this.slv_port_axi = slv_port_vif; - this.mst_port_axi = mst_port_vif; - this.tests_expected = 0 ; - this.tests_conducted = 0 ; - this.tests_failed = 0 ; - this.exp_slv_port_b_queue = new ; - this.exp_slv_port_r_queue = new ; - this.exp_mst_port_aw_queue = new ; - this.exp_mst_port_ar_queue = new ; - this.act_mst_port_ar_queue = new ; - this.act_slv_port_ar_queue = new ; - this.cnt_sem = new(1) ; - end - endfunction - - task cycle_start; - #TimeTest; - endtask: cycle_start - - task cycle_end; - @(posedge slv_port_axi.clk_i); - endtask: cycle_end - - /************** - * Monitors * - **************/ - - /* - * You need to override this task. Use it to push the expected AW requests on - * the slave side, and the B and R responses expected on the master side. - */ - virtual task automatic mon_slv_port_aw () ; - $error("This task needs to be overridden."); - endtask : mon_slv_port_aw - - /* - * You need to override this task. Use it to push the expected W requests on - * the slave side. - */ - virtual task automatic mon_slv_port_w () ; - $error("This task needs to be overridden."); - endtask : mon_slv_port_w - - /* - * You need to override this task. Use it to push the expected R responses on - * the master side. - */ - virtual task automatic mon_mst_port_r () ; - $error("This task needs to be overridden."); - endtask : mon_mst_port_r - - /* - * You need to override this task. Use it to push the expected AR requests on - * the slave side, and the R responses expected on the master side. - */ - virtual task automatic mon_slv_port_ar () ; - $error("This task needs to be overridden."); - endtask : mon_slv_port_ar - - /* - * This tasks stores the beats seen by the AR, AW and W channels - * into the respective queues. - */ - virtual task automatic store_channels (); - if (slv_port_axi.ar_valid && slv_port_axi.ar_ready) - act_slv_port_ar_queue.push(slv_port_axi.ar_id, - '{ - axi_id : slv_port_axi.ar_id , - axi_burst: slv_port_axi.ar_burst, - axi_size : slv_port_axi.ar_size , - axi_addr : slv_port_axi.ar_addr , - axi_len : slv_port_axi.ar_len , - axi_cache: slv_port_axi.ar_cache - }); - - if (slv_port_axi.aw_valid && slv_port_axi.aw_ready) begin - act_slv_port_aw_queue.push_back('{ - axi_id : slv_port_axi.aw_id , - axi_burst: slv_port_axi.aw_burst, - axi_size : slv_port_axi.aw_size , - axi_addr : slv_port_axi.aw_addr , - axi_len : slv_port_axi.aw_len , - axi_cache: slv_port_axi.aw_cache - }); - - // This request generates an R response. - // Push this to the AR queue. - if (slv_port_axi.aw_atop[axi_pkg::ATOP_R_RESP]) - act_slv_port_ar_queue.push(slv_port_axi.aw_id, - '{ - axi_id : slv_port_axi.aw_id , - axi_burst: slv_port_axi.aw_burst, - axi_size : slv_port_axi.aw_size , - axi_addr : slv_port_axi.aw_addr , - axi_len : slv_port_axi.aw_len , - axi_cache: slv_port_axi.aw_cache - }); - end - - if (slv_port_axi.w_valid && slv_port_axi.w_ready) - this.act_slv_port_w_queue.push_back('{ - axi_id : {AxiIdWidth{1'b?}} , - axi_data: slv_port_axi.w_data, - axi_strb: slv_port_axi.w_strb, - axi_last: slv_port_axi.w_last - }); - - if (slv_port_axi.r_valid && slv_port_axi.r_ready) - this.act_slv_port_r_queue.push_back('{ - axi_id : slv_port_axi.r_id , - axi_data: slv_port_axi.r_data , - axi_strb: {AxiSlvPortStrbWidth{1'b?}}, - axi_last: slv_port_axi.r_last - }); - - if (mst_port_axi.ar_valid && mst_port_axi.ar_ready) - act_mst_port_ar_queue.push(mst_port_axi.ar_id, - '{ - axi_id : mst_port_axi.ar_id , - axi_burst: mst_port_axi.ar_burst, - axi_size : mst_port_axi.ar_size , - axi_addr : mst_port_axi.ar_addr , - axi_len : mst_port_axi.ar_len , - axi_cache: mst_port_axi.ar_cache - }); - - if (mst_port_axi.aw_valid && mst_port_axi.aw_ready) begin - act_mst_port_aw_queue.push_back('{ - axi_id : mst_port_axi.aw_id , - axi_burst: mst_port_axi.aw_burst, - axi_size : mst_port_axi.aw_size , - axi_addr : mst_port_axi.aw_addr , - axi_len : mst_port_axi.aw_len , - axi_cache: mst_port_axi.aw_cache - }); - - // This request generates an R response. - // Push this to the AR queue. - if (mst_port_axi.aw_atop[axi_pkg::ATOP_R_RESP]) - act_mst_port_ar_queue.push(mst_port_axi.aw_id, - '{ - axi_id : mst_port_axi.aw_id , - axi_burst: mst_port_axi.aw_burst, - axi_size : mst_port_axi.aw_size , - axi_addr : mst_port_axi.aw_addr , - axi_len : mst_port_axi.aw_len , - axi_cache: mst_port_axi.aw_cache - }); - end - - if (mst_port_axi.w_valid && mst_port_axi.w_ready) - this.act_mst_port_w_queue.push_back('{ - axi_id : {AxiIdWidth{1'b?}} , - axi_data: mst_port_axi.w_data, - axi_strb: mst_port_axi.w_strb, - axi_last: mst_port_axi.w_last - }); - endtask - - /* - * This task monitors the master port of the DW converter. Every time it gets an AW transaction, - * it gets checked for its contents against the expected beat on the `exp_aw_queue`. - */ - task automatic mon_mst_port_aw (); - exp_ax_t exp_aw; - if (mst_port_axi.aw_valid && mst_port_axi.aw_ready) begin - // Test if the AW beat was expected - exp_aw = this.exp_mst_port_aw_queue.pop_id(mst_port_axi.aw_id); - if (exp_aw.axi_id != mst_port_axi.aw_id) begin - incr_failed_tests(1) ; - $warning("Slave: Unexpected AW with ID: %b", mst_port_axi.aw_id); - end - if (exp_aw.axi_addr != mst_port_axi.aw_addr) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AW with ID: %b and ADDR: %h, exp: %h", - mst_port_axi.aw_id, mst_port_axi.aw_addr, exp_aw.axi_addr); - end - if (exp_aw.axi_len != mst_port_axi.aw_len) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AW with ID: %b and LEN: %h, exp: %h", - mst_port_axi.aw_id, mst_port_axi.aw_len, exp_aw.axi_len); - end - if (exp_aw.axi_burst != mst_port_axi.aw_burst) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AW with ID: %b and BURST: %h, exp: %h", - mst_port_axi.aw_id, mst_port_axi.aw_burst, exp_aw.axi_burst); - end - if (exp_aw.axi_size != mst_port_axi.aw_size) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AW with ID: %b and SIZE: %h, exp: %h", - mst_port_axi.aw_id, mst_port_axi.aw_size, exp_aw.axi_size); - end - if (exp_aw.axi_cache != mst_port_axi.aw_cache) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AW with ID: %b and CACHE: %b, exp: %b", - mst_port_axi.aw_id, mst_port_axi.aw_cache, exp_aw.axi_cache); - end - incr_conducted_tests(6); - end - endtask : mon_mst_port_aw - - /* - * This task compares the expected and actual W beats on the master port. - */ - task automatic mon_mst_port_w (); - exp_mst_rw_t exp_w, act_w; - while (this.exp_mst_port_w_queue.size() != 0 && this.act_mst_port_w_queue.size() != 0) begin - exp_w = this.exp_mst_port_w_queue.pop_front(); - act_w = this.act_mst_port_w_queue.pop_front(); - // Do the checks - if (exp_w.axi_data != act_w.axi_data) begin - incr_failed_tests(1); - $warning("Slave: Unexpected W with DATA: %h, exp: %h", - act_w.axi_data, exp_w.axi_data); - end - if (exp_w.axi_strb != act_w.axi_strb) begin - incr_failed_tests(1); - $warning("Slave: Unexpected W with STRB: %h, exp: %h", - act_w.axi_strb, exp_w.axi_strb); - end - if (exp_w.axi_last != act_w.axi_last) begin - incr_failed_tests(1); - $warning("Slave: Unexpected W with LAST: %b, exp: %b", - act_w.axi_last, exp_w.axi_last); - end - incr_conducted_tests(3); - end - endtask : mon_mst_port_w - - /* - * This task checks if a B response is allowed on a slave port of the DW converter. - */ - task automatic mon_slv_port_b (); - exp_b_t exp_b; - axi_id_t axi_b_id; - if (slv_port_axi.b_valid && slv_port_axi.b_ready) begin - incr_conducted_tests(1); - axi_b_id = slv_port_axi.b_id; - $display("%0tns > Master: Got last B with ID: %b", - $time, axi_b_id); - if (this.exp_slv_port_b_queue.empty()) begin - incr_failed_tests(1) ; - $warning("Master: unexpected B beat with ID: %b detected!", axi_b_id); - end else begin - exp_b = this.exp_slv_port_b_queue.pop_id(axi_b_id); - if (axi_b_id != exp_b.axi_id) begin - incr_failed_tests(1) ; - $warning("Master: got unexpected B with ID: %b", axi_b_id); - end - end - end - endtask : mon_slv_port_b - - /* - * This task monitors a the master port of the DW converter and checks - * if the AR beats were all expected. - */ - task automatic mon_mst_port_ar (); - exp_ax_t exp_ar; - if (mst_port_axi.ar_valid && mst_port_axi.ar_ready) begin - // Test if the AR beat was expected - exp_ar = this.exp_mst_port_ar_queue.pop_id(mst_port_axi.ar_id); - if (exp_ar.axi_id != mst_port_axi.ar_id) begin - incr_failed_tests(1) ; - $warning("Slave: Unexpected AR with ID: %b", mst_port_axi.ar_id); - end - if (exp_ar.axi_addr != mst_port_axi.ar_addr) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AR with ID: %b and ADDR: %h, exp: %h", - mst_port_axi.ar_id, mst_port_axi.ar_addr, exp_ar.axi_addr); - end - if (exp_ar.axi_len != mst_port_axi.ar_len) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AR with ID: %b and LEN: %h, exp: %h", - mst_port_axi.ar_id, mst_port_axi.ar_len, exp_ar.axi_len); - end - if (exp_ar.axi_burst != mst_port_axi.ar_burst) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AR with ID: %b and BURST: %h, exp: %h", - mst_port_axi.ar_id, mst_port_axi.ar_burst, exp_ar.axi_burst); - end - if (exp_ar.axi_size != mst_port_axi.ar_size) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AR with ID: %b and SIZE: %h, exp: %h", - mst_port_axi.ar_id, mst_port_axi.ar_size, exp_ar.axi_size); - end - if (exp_ar.axi_cache != mst_port_axi.ar_cache) begin - incr_failed_tests(1); - $warning("Slave: Unexpected AR with ID: %b and CACHE: %b, exp: %b", - mst_port_axi.ar_id, mst_port_axi.ar_cache, exp_ar.axi_cache); - end - incr_conducted_tests(6); - end - endtask : mon_mst_port_ar - - /* - * This task does the R channel monitoring on a slave port. It compares the last flags, - * which are determined by the sequence of previously sent AR vectors. - */ - task automatic mon_slv_port_r (); - exp_slv_rw_t exp_r; - if (act_slv_port_r_queue.size() != 0) begin - exp_slv_rw_t act_r = act_slv_port_r_queue[0] ; - if (exp_slv_port_r_queue.queues[act_r.axi_id].size() != 0) begin - exp_r = exp_slv_port_r_queue.pop_id(act_r.axi_id); - void'(act_slv_port_r_queue.pop_front()); - - // Do the checks - if (exp_r.axi_id != act_r.axi_id) begin - incr_failed_tests(1); - $warning("Slave: Unexpected R with ID: %b", - act_r.axi_id); - end - if (exp_r.axi_last != act_r.axi_last) begin - incr_failed_tests(1); - $warning("Slave: Unexpected R with ID: %b and LAST: %b, exp: %b", - act_r.axi_id, act_r.axi_last, exp_r.axi_last); - end - if (exp_r.axi_data != act_r.axi_data) begin - incr_failed_tests(1); - $warning("Slave: Unexpected R with ID: %b and DATA: %h, exp: %h", - act_r.axi_id, act_r.axi_data, exp_r.axi_data); - end - incr_conducted_tests(3); - end - end - endtask : mon_slv_port_r - - // Some tasks to manage bookkeeping of the tests conducted. - task incr_expected_tests(input int unsigned times); - cnt_sem.get() ; - this.tests_expected += times; - cnt_sem.put() ; - endtask : incr_expected_tests - - task incr_conducted_tests(input int unsigned times); - cnt_sem.get() ; - this.tests_conducted += times; - cnt_sem.put() ; - endtask : incr_conducted_tests - - task incr_failed_tests(input int unsigned times); - cnt_sem.get() ; - this.tests_failed += times; - cnt_sem.put() ; - endtask : incr_failed_tests - - /* - * This task invokes the various monitoring tasks. First, all processes that only - * push something in the FIFOs are invoked. After they are finished, the processes - * that pop something from them are invoked. - */ - task run(); - forever begin - // At every cycle, spawn some monitoring processes. - cycle_start(); - - // Execute all processes that push something into the queues - PushMon: fork - proc_mst_aw : mon_slv_port_aw(); - proc_mst_ar : mon_slv_port_ar(); - proc_mst_w : mon_slv_port_w() ; - proc_slv_r : mon_mst_port_r() ; - proc_store_channel: store_channels() ; - join: PushMon - - // These only pop something from the queues - PopMon: fork - proc_slv_aw: mon_mst_port_aw(); - proc_mst_b : mon_slv_port_b() ; - proc_slv_ar: mon_mst_port_ar(); - proc_mst_r : mon_slv_port_r() ; - join : PopMon - - // Check the slave W FIFOs last - proc_check_slv_w: mon_mst_port_w(); - - cycle_end(); - end - endtask : run - - task print_result() ; - $info("Simulation has ended!") ; - $display("Tests Expected: %d", this.tests_expected) ; - $display("Tests Conducted: %d", this.tests_conducted); - $display("Tests Failed: %d", this.tests_failed) ; - if (tests_failed > 0) begin - $error("Simulation encountered unexpected transactions!"); - end - endtask : print_result - - endclass : axi_dw_monitor - - /************* - * UPSIZER * - *************/ - - class axi_dw_upsizer_monitor #( - parameter int unsigned AxiAddrWidth , - parameter int unsigned AxiSlvPortDataWidth, - parameter int unsigned AxiMstPortDataWidth, - parameter int unsigned AxiIdWidth , - parameter int unsigned AxiUserWidth , - // Stimuli application and test time - parameter time TimeTest - ) extends axi_dw_monitor #( - .AxiAddrWidth (AxiAddrWidth ), - .AxiSlvPortDataWidth(AxiSlvPortDataWidth), - .AxiMstPortDataWidth(AxiMstPortDataWidth), - .AxiIdWidth (AxiIdWidth ), - .AxiUserWidth (AxiUserWidth ), - .TimeTest (TimeTest ) - ); - - local static shortint unsigned slv_port_r_cnt[axi_id_t]; - local static shortint unsigned mst_port_r_cnt[axi_id_t]; - local static shortint unsigned slv_port_w_cnt; - local static shortint unsigned mst_port_w_cnt; - local static exp_mst_rw_t mst_port_w; - local static shortint unsigned mst_port_w_pnt; - - /***************** - * Constructor * - *****************/ - - function new ( - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiSlvPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) slv_port_vif, - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiMstPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) mst_port_vif - ); - begin - super.new(slv_port_vif, mst_port_vif); - slv_port_w_cnt = '0; - mst_port_w_cnt = '0; - mst_port_w_pnt = '1; - mst_port_w = '0; - for (int unsigned id = 0; id < 2**AxiIdWidth; id++) begin - slv_port_r_cnt[id] = '0; - mst_port_r_cnt[id] = '0; - end - end - endfunction - - /************** - * Monitors * - **************/ - - task automatic mon_slv_port_aw (); - exp_ax_t exp_aw; - - if (slv_port_axi.aw_valid && slv_port_axi.aw_ready) begin - // Non-modifiable transaction - if (!axi_pkg::modifiable(slv_port_axi.aw_cache)) begin - // We expect that the transaction will not be modified - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : slv_port_axi.aw_len , - axi_burst: slv_port_axi.aw_burst, - axi_size : slv_port_axi.aw_size , - axi_cache: slv_port_axi.aw_cache - } ; - end - // Modifiable transaction - else begin - case (slv_port_axi.aw_burst) - // Passthrough upsize - axi_pkg::BURST_FIXED: begin - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : slv_port_axi.aw_len , - axi_burst: slv_port_axi.aw_burst, - axi_size : slv_port_axi.aw_size , - axi_cache: slv_port_axi.aw_cache - }; - end - // INCR upsize - axi_pkg::BURST_INCR: begin - automatic axi_addr_t aligned_start = axi_pkg::aligned_addr(slv_port_axi.aw_addr, AxiMstPortMaxSize) ; - automatic axi_addr_t aligned_end = axi_pkg::aligned_addr(axi_pkg::aligned_addr(slv_port_axi.aw_addr, slv_port_axi.aw_size) + (unsigned'(slv_port_axi.aw_len) << slv_port_axi.aw_size), AxiMstPortMaxSize); - - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : (aligned_end - aligned_start) >> AxiMstPortMaxSize , - axi_burst: slv_port_axi.aw_burst , - axi_size : slv_port_axi.aw_len == 0 ? slv_port_axi.aw_size : AxiMstPortMaxSize, - axi_cache: slv_port_axi.aw_cache - }; - end - // WRAP upsize - axi_pkg::BURST_WRAP: begin - exp_aw = '0; - $warning("WRAP bursts are not supported."); - end - endcase - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - $display("%0tns > Master: AW with ID: %b", - $time, slv_port_axi.aw_id); - end - - // Populate the expected B responses - this.exp_slv_port_b_queue.push(slv_port_axi.aw_id, '{ - axi_id : slv_port_axi.aw_id, - axi_last: 1'b1 - }) ; - incr_expected_tests(1) ; - $display(" Expect B response.") ; - end - endtask : mon_slv_port_aw - - task automatic mon_slv_port_w (); - if (act_slv_port_w_queue.size() != 0) begin - exp_slv_rw_t act_slv_w = act_slv_port_w_queue[0]; - - if (act_mst_port_aw_queue.size() != 0 && act_slv_port_aw_queue.size() != 0) begin - // Retrieve the AW requests related to this W beat - exp_ax_t act_mst_aw = act_mst_port_aw_queue[0]; - exp_ax_t act_slv_aw = act_slv_port_aw_queue[0]; - - // Calculate the offsets - shortint unsigned mst_port_lower_byte = - axi_pkg::beat_lower_byte(act_mst_aw.axi_addr, act_mst_aw.axi_size, act_mst_aw.axi_len, act_mst_aw.axi_burst, AxiMstPortStrbWidth, mst_port_w_cnt); - shortint unsigned mst_port_upper_byte = - axi_pkg::beat_upper_byte(act_mst_aw.axi_addr, act_mst_aw.axi_size, act_mst_aw.axi_len, act_mst_aw.axi_burst, AxiMstPortStrbWidth, mst_port_w_cnt); - shortint unsigned slv_port_lower_byte = - axi_pkg::beat_lower_byte(act_slv_aw.axi_addr, act_slv_aw.axi_size, act_slv_aw.axi_len, act_slv_aw.axi_burst, AxiSlvPortStrbWidth, slv_port_w_cnt); - shortint unsigned slv_port_upper_byte = - axi_pkg::beat_upper_byte(act_slv_aw.axi_addr, act_slv_aw.axi_size, act_slv_aw.axi_len, act_slv_aw.axi_burst, AxiSlvPortStrbWidth, slv_port_w_cnt); - - shortint unsigned bytes_copied = 0; - // Pointer inside the outcoming word - if (mst_port_w_pnt == '1) - mst_port_w_pnt = mst_port_lower_byte; - - mst_port_w.axi_last = mst_port_w_cnt == act_mst_aw.axi_len; - for (shortint unsigned b = slv_port_lower_byte; b <= slv_port_upper_byte; b++) begin - if (b + mst_port_w_pnt - slv_port_lower_byte == AxiMstPortStrbWidth) - break; - mst_port_w.axi_data[8*(b + mst_port_w_pnt - slv_port_lower_byte) +: 8] = act_slv_w.axi_data[8*b +: 8]; - mst_port_w.axi_strb[b + mst_port_w_pnt - slv_port_lower_byte] = act_slv_w.axi_strb[b] ; - bytes_copied++; - end - - // Increment the len counters - slv_port_w_cnt++ ; - mst_port_w_pnt += bytes_copied; - - if (act_mst_aw.axi_burst == axi_pkg::BURST_FIXED // No upsizing - || mst_port_w_pnt == AxiMstPortStrbWidth // Filled up an outcoming W beat - || act_slv_w.axi_last // Last beat of a W burst - ) begin - // Don't care for the bits outside these accessed by this request - for (int unsigned b = 0; b < AxiMstPortStrbWidth; b++) - if (!(mst_port_lower_byte <= b && b <= mst_port_upper_byte)) - mst_port_w.axi_data[8*b +: 8] = {8{1'b?}}; - - this.exp_mst_port_w_queue.push_back(mst_port_w); - incr_expected_tests(3) ; - - // Increment the len counter - mst_port_w_cnt++; - - // Reset W beat - mst_port_w = '0; - mst_port_w_pnt = '1; - end - - // Pop the AW request from the queues - if (slv_port_w_cnt == act_slv_aw.axi_len + 1) begin - void'(act_slv_port_aw_queue.pop_front()); - slv_port_w_cnt = 0; - end - if (mst_port_w_cnt == act_mst_aw.axi_len + 1) begin - void'(act_mst_port_aw_queue.pop_front()); - mst_port_w_cnt = 0; - end - - // Pop the W request - void'(act_slv_port_w_queue.pop_front()); - end - end - endtask : mon_slv_port_w - - task automatic mon_slv_port_ar (); - exp_ax_t exp_slv_ar; - exp_b_t exp_mst_r; - - if (slv_port_axi.ar_valid && slv_port_axi.ar_ready) begin - // Non-modifiable transaction - if (!axi_pkg::modifiable(slv_port_axi.ar_cache)) begin - // We expect that the transaction will not be modified - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : slv_port_axi.ar_len , - axi_burst: slv_port_axi.ar_burst, - axi_size : slv_port_axi.ar_size , - axi_cache: slv_port_axi.ar_cache - }; - end - // Modifiable transaction - else begin - case (slv_port_axi.ar_burst) - // Passthrough upsize - axi_pkg::BURST_FIXED: begin - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : slv_port_axi.ar_len , - axi_burst: slv_port_axi.ar_burst, - axi_size : slv_port_axi.ar_size , - axi_cache: slv_port_axi.ar_cache - }; - end - // INCR upsize - axi_pkg::BURST_INCR: begin - automatic axi_addr_t aligned_start = axi_pkg::aligned_addr(slv_port_axi.ar_addr, AxiMstPortMaxSize) ; - automatic axi_addr_t aligned_end = axi_pkg::aligned_addr(axi_pkg::aligned_addr(slv_port_axi.ar_addr, slv_port_axi.ar_size) + (unsigned'(slv_port_axi.ar_len) << slv_port_axi.ar_size), AxiMstPortMaxSize); - - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : (aligned_end - aligned_start) >> AxiMstPortMaxSize , - axi_burst: slv_port_axi.ar_burst , - axi_size : slv_port_axi.ar_len == 0 ? slv_port_axi.ar_size : AxiMstPortMaxSize, - axi_cache: slv_port_axi.ar_cache - }; - end - // WRAP upsize - axi_pkg::BURST_WRAP: begin - exp_slv_ar = '0; - $warning("WRAP bursts are not supported."); - end - endcase - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - $display("%0tns > Master: AR with ID: %b", - $time, slv_port_axi.ar_id); - end - end - endtask : mon_slv_port_ar - - task automatic mon_mst_port_r (); - if (mst_port_axi.r_valid && mst_port_axi.r_ready) begin - // Retrieve the AR requests related to this R beat - exp_ax_t act_mst_ar = act_mst_port_ar_queue.get(mst_port_axi.r_id); - exp_ax_t act_slv_ar = act_slv_port_ar_queue.get(mst_port_axi.r_id); - axi_id_t id = mst_port_axi.r_id ; - - // Calculate the offsets inside the incoming word - shortint unsigned mst_port_lower_byte = - axi_pkg::beat_lower_byte(act_mst_ar.axi_addr, act_mst_ar.axi_size, act_mst_ar.axi_len, act_mst_ar.axi_burst, AxiMstPortStrbWidth, mst_port_r_cnt[id]); - shortint unsigned mst_port_upper_byte = - axi_pkg::beat_upper_byte(act_mst_ar.axi_addr, act_mst_ar.axi_size, act_mst_ar.axi_len, act_mst_ar.axi_burst, AxiMstPortStrbWidth, mst_port_r_cnt[id]); - // Pointer inside the incoming word - shortint unsigned mst_port_data_pointer = mst_port_lower_byte; - - // Conversion ratio. How many R beats are generated from this incoming R beat. - int unsigned conversion_ratio = axi_pkg::modifiable(act_mst_ar.axi_cache) ? conv_ratio(act_mst_ar.axi_size, act_slv_ar.axi_size) : 1; - - // Several R beats generated from this incoming R beat - for (int unsigned beat = 0; beat < conversion_ratio; beat++) begin - exp_slv_rw_t act_slv_r = '0; - - // Calculate the offsets inside the outcoming word - shortint unsigned slv_port_lower_byte = - axi_pkg::beat_lower_byte(act_slv_ar.axi_addr, act_slv_ar.axi_size, act_slv_ar.axi_len, act_slv_ar.axi_burst, AxiSlvPortStrbWidth, slv_port_r_cnt[id]); - shortint unsigned slv_port_upper_byte = - axi_pkg::beat_upper_byte(act_slv_ar.axi_addr, act_slv_ar.axi_size, act_slv_ar.axi_len, act_slv_ar.axi_burst, AxiSlvPortStrbWidth, slv_port_r_cnt[id]); - - shortint unsigned bytes_copied = 0; - - act_slv_r.axi_id = mst_port_axi.r_id ; - act_slv_r.axi_last = slv_port_r_cnt[id] == act_slv_ar.axi_len; - act_slv_r.axi_data = {AxiSlvPortDataWidth{1'b?}} ; - act_slv_r.axi_strb = {AxiSlvPortStrbWidth{1'b?}} ; - for (shortint unsigned b = mst_port_data_pointer; b <= mst_port_upper_byte; b++) begin - act_slv_r.axi_data[8*(b + slv_port_lower_byte - mst_port_data_pointer) +: 8] = mst_port_axi.r_data[8*b +: 8]; - bytes_copied++; - if (b + slv_port_lower_byte - mst_port_data_pointer == slv_port_upper_byte) - break; - end - this.exp_slv_port_r_queue.push(act_slv_r.axi_id, act_slv_r); - incr_expected_tests(3) ; - - // Increment the len counters - slv_port_r_cnt[id]++ ; - mst_port_data_pointer += bytes_copied; - - // Used the whole R beat - if (mst_port_data_pointer == AxiMstPortStrbWidth) - break; - // Finished the R beat - if (act_slv_r.axi_last) - break; - end - - // Increment the len counter - mst_port_r_cnt[id]++; - - // Pop the AR request from the queues - if (mst_port_r_cnt[id] == act_mst_ar.axi_len + 1) begin - void'(act_mst_port_ar_queue.pop_id(act_mst_ar.axi_id)); - mst_port_r_cnt[id] = 0; - end - if (slv_port_r_cnt[id] == act_slv_ar.axi_len + 1) begin - void'(act_slv_port_ar_queue.pop_id(act_slv_ar.axi_id)); - slv_port_r_cnt[id] = 0; - end - end - endtask: mon_mst_port_r - endclass : axi_dw_upsizer_monitor - - /*************** - * DOWNSIZER * - ***************/ - - class axi_dw_downsizer_monitor #( - parameter int unsigned AxiAddrWidth , - parameter int unsigned AxiSlvPortDataWidth, - parameter int unsigned AxiMstPortDataWidth, - parameter int unsigned AxiIdWidth , - parameter int unsigned AxiUserWidth , - // Stimuli application and test time - parameter time TimeTest - ) extends axi_dw_monitor #( - .AxiAddrWidth (AxiAddrWidth ), - .AxiSlvPortDataWidth(AxiSlvPortDataWidth), - .AxiMstPortDataWidth(AxiMstPortDataWidth), - .AxiIdWidth (AxiIdWidth ), - .AxiUserWidth (AxiUserWidth ), - .TimeTest (TimeTest ) - ); - - local static shortint unsigned slv_port_r_cnt[axi_id_t]; - local static shortint unsigned mst_port_r_cnt[axi_id_t]; - local static exp_slv_rw_t slv_port_r[axi_id_t]; - local static shortint unsigned slv_port_r_pnt[axi_id_t]; - local static shortint unsigned slv_port_w_cnt; - local static shortint unsigned mst_port_w_cnt; - - /********************** - * Helper functions * - **********************/ - - /* - * Returns how many beats of the incoming AXI transaction will be dropped after downsizing - * due to an unaligned memory address. - */ - function automatic len_t aligned_adjustment(axi_addr_t addr, axi_pkg::size_t size) ; - return (addr & size_mask(size) & ~size_mask(AxiMstPortMaxSize))/axi_pkg::num_bytes(AxiMstPortMaxSize); - endfunction: aligned_adjustment - - /***************** - * Constructor * - *****************/ - - function new ( - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiSlvPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) slv_port_vif, - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AxiAddrWidth ), - .AXI_DATA_WIDTH(AxiMstPortDataWidth), - .AXI_ID_WIDTH (AxiIdWidth ), - .AXI_USER_WIDTH(AxiUserWidth ) - ) mst_port_vif - ); - begin - super.new(slv_port_vif, mst_port_vif); - - slv_port_w_cnt = 0; - mst_port_w_cnt = 0; - for (int unsigned id = 0; id < 2**AxiIdWidth; id++) begin - slv_port_r_cnt[id] = '0; - mst_port_r_cnt[id] = '0; - slv_port_r[id] = '0; - slv_port_r_pnt[id] = '1; - end - end - endfunction - - /************** - * Monitors * - **************/ - - task automatic mon_slv_port_aw (); - exp_ax_t exp_aw; - - if (slv_port_axi.aw_valid && slv_port_axi.aw_ready) begin - case (slv_port_axi.aw_burst) - axi_pkg::BURST_INCR: begin - // Transaction unchanged - if (conv_ratio(slv_port_axi.aw_size, AxiMstPortMaxSize) == 1) begin - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : slv_port_axi.aw_len , - axi_burst: slv_port_axi.aw_burst, - axi_size : slv_port_axi.aw_size , - axi_cache: slv_port_axi.aw_cache - }; - - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - end - // INCR downsize - else begin - automatic int unsigned num_beats = (slv_port_axi.aw_len + 1) * conv_ratio(slv_port_axi.aw_size, AxiMstPortMaxSize) - aligned_adjustment(slv_port_axi.aw_addr, slv_port_axi.aw_size); - // One burst - if (num_beats <= 256) begin - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : num_beats - 1 , - axi_burst: slv_port_axi.aw_burst, - axi_size : AxiMstPortMaxSize , - axi_cache: slv_port_axi.aw_cache - }; - - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - end - // Need to split the incoming burst into several INCR bursts - else begin - automatic axi_addr_t burst_addr; - automatic len_t burst_len ; - - // First burst is a "partial" burst - burst_len = 255 - aligned_adjustment(slv_port_axi.aw_addr, slv_port_axi.aw_size); - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : burst_len , - axi_burst: slv_port_axi.aw_burst, - axi_size : AxiMstPortMaxSize , - axi_cache: slv_port_axi.aw_cache - } ; - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - - // Push the other bursts in a loop - num_beats = num_beats - burst_len - 1 ; - burst_addr = axi_pkg::beat_addr(axi_pkg::aligned_addr(slv_port_axi.aw_addr, AxiMstPortMaxSize), AxiMstPortMaxSize, burst_len, axi_pkg::BURST_INCR, burst_len+1); - while (num_beats != 0) begin - burst_len = num_beats >= 256 ? 255 : num_beats - 1; - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : burst_addr , - axi_len : burst_len , - axi_burst: slv_port_axi.aw_burst, - axi_size : AxiMstPortMaxSize , - axi_cache: slv_port_axi.aw_cache - } ; - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - - num_beats = num_beats - burst_len - 1 ; - burst_addr = axi_pkg::beat_addr(burst_addr, AxiMstPortMaxSize, burst_len, axi_pkg::BURST_INCR, burst_len+1); - end; - end - end - end - // Passthrough downsize - axi_pkg::BURST_FIXED: begin - // Transaction unchanged - if (conv_ratio(slv_port_axi.aw_size, AxiMstPortMaxSize) == 1) begin - exp_aw = '{ - axi_id : slv_port_axi.aw_id , - axi_addr : slv_port_axi.aw_addr , - axi_len : slv_port_axi.aw_len , - axi_burst: slv_port_axi.aw_burst, - axi_size : slv_port_axi.aw_size , - axi_cache: slv_port_axi.aw_cache - }; - - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - end - // Split into master_axi.aw_len + 1 INCR bursts - else begin - for (int unsigned j = 0; j <= slv_port_axi.aw_len; j++) begin - exp_aw.axi_id = slv_port_axi.aw_id ; - exp_aw.axi_addr = slv_port_axi.aw_addr ; - exp_aw.axi_burst = axi_pkg::BURST_INCR ; - exp_aw.axi_size = AxiMstPortMaxSize ; - exp_aw.axi_cache = slv_port_axi.aw_cache; - if (conv_ratio(slv_port_axi.aw_size, AxiMstPortMaxSize) >= aligned_adjustment(slv_port_axi.aw_addr, slv_port_axi.aw_size) + 1) - exp_aw.axi_len = conv_ratio(slv_port_axi.aw_size, AxiMstPortMaxSize) - aligned_adjustment(slv_port_axi.aw_addr, slv_port_axi.aw_size) - 1; - else - exp_aw.axi_len = 0; - - this.exp_mst_port_aw_queue.push(slv_port_axi.aw_id, exp_aw); - incr_expected_tests(6) ; - end - end - end - // WRAP downsize - axi_pkg::BURST_WRAP: begin - exp_aw = '0; - $warning("WRAP bursts are not supported."); - end - endcase - - $display("%0tns > Master: AW with ID: %b", - $time, slv_port_axi.aw_id); - - // Populate the expected B queue - this.exp_slv_port_b_queue.push(slv_port_axi.aw_id, '{ - axi_id : slv_port_axi.aw_id, - axi_last: 1'b1 - }) ; - incr_expected_tests(1) ; - $display(" Expect B response."); - end - endtask : mon_slv_port_aw - - task automatic mon_slv_port_w (); - if (act_slv_port_w_queue.size() != 0) begin - exp_slv_rw_t act_slv_w = act_slv_port_w_queue[0]; - - if (act_mst_port_aw_queue.size() != 0 && act_slv_port_aw_queue.size() != 0) begin - // Retrieve the AW requests related to this W beat - exp_ax_t act_mst_aw = act_mst_port_aw_queue[0]; - exp_ax_t act_slv_aw = act_slv_port_aw_queue[0]; - - // Address of the current beat - axi_addr_t slv_aw_addr = axi_pkg::beat_addr(act_slv_aw.axi_addr, act_slv_aw.axi_size, act_slv_aw.axi_len, act_slv_aw.axi_burst, slv_port_w_cnt); - - // Calculate the offsets inside the incoming word - shortint unsigned slv_port_lower_byte = - axi_pkg::beat_lower_byte(act_slv_aw.axi_addr, act_slv_aw.axi_size, act_slv_aw.axi_len, act_slv_aw.axi_burst, AxiSlvPortStrbWidth, slv_port_w_cnt); - // Pointer inside the incoming word - shortint unsigned slv_port_data_pointer = slv_port_lower_byte; - - // Several W beats generated from this incoming W beat - int unsigned beat_cnt = conv_ratio(act_slv_aw.axi_size, AxiMstPortMaxSize) - aligned_adjustment(slv_aw_addr, act_slv_aw.axi_size); - - for (int unsigned beat = 0; beat < beat_cnt; beat++) begin - exp_mst_rw_t act_mst_w = '0; - - // Calculate the offsets inside the outcoming word - shortint unsigned mst_port_lower_byte = - axi_pkg::beat_lower_byte(act_mst_aw.axi_addr, act_mst_aw.axi_size, act_mst_aw.axi_len, act_mst_aw.axi_burst, AxiMstPortStrbWidth, mst_port_w_cnt); - shortint unsigned mst_port_upper_byte = - axi_pkg::beat_upper_byte(act_mst_aw.axi_addr, act_mst_aw.axi_size, act_mst_aw.axi_len, act_mst_aw.axi_burst, AxiMstPortStrbWidth, mst_port_w_cnt); - - shortint unsigned bytes_copied = 0; - - act_mst_w.axi_id = act_mst_aw.axi_id ; - act_mst_w.axi_last = mst_port_w_cnt == act_mst_aw.axi_len; - act_mst_w.axi_data = '0 ; - for (shortint unsigned b = slv_port_data_pointer; b < AxiSlvPortStrbWidth; b++) begin - if (b + mst_port_lower_byte - slv_port_data_pointer == AxiMstPortStrbWidth) - break; - act_mst_w.axi_data[8*(b + mst_port_lower_byte - slv_port_data_pointer) +: 8] = act_slv_w.axi_data[8*b +: 8]; - act_mst_w.axi_strb[b + mst_port_lower_byte - slv_port_data_pointer] = act_slv_w.axi_strb[b] ; - bytes_copied++; - end - // Don't care for the bits outside these accessed by this request - for (int unsigned b = 0; b < AxiMstPortStrbWidth; b++) - if (!(mst_port_lower_byte <= b && b <= mst_port_upper_byte)) - act_mst_w.axi_data[8*b +: 8] = {8{1'b?}}; - - this.exp_mst_port_w_queue.push_back(act_mst_w); - incr_expected_tests(3) ; - - // Increment the len counters - mst_port_w_cnt++ ; - slv_port_data_pointer += bytes_copied; - - // Used the whole W beat - if (slv_port_data_pointer == AxiSlvPortStrbWidth) - break; - end - - // Increment the len counter - slv_port_w_cnt++; - - // Pop the AW request from the queues - if (slv_port_w_cnt == act_slv_aw.axi_len + 1) begin - void'(act_slv_port_aw_queue.pop_front()); - slv_port_w_cnt = 0; - end - if (mst_port_w_cnt == act_mst_aw.axi_len + 1) begin - void'(act_mst_port_aw_queue.pop_front()); - mst_port_w_cnt = 0; - end - - // Pop the W request - void'(act_slv_port_w_queue.pop_front()); - end - end - endtask: mon_slv_port_w - - task automatic mon_slv_port_ar (); - exp_ax_t exp_slv_ar; - exp_b_t exp_mst_r; - - if (slv_port_axi.ar_valid && slv_port_axi.ar_ready) begin - case (slv_port_axi.ar_burst) - axi_pkg::BURST_INCR: begin - // Transaction unchanged - if (conv_ratio(slv_port_axi.ar_size, AxiMstPortMaxSize) == 1) begin - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : slv_port_axi.ar_len , - axi_burst: slv_port_axi.ar_burst, - axi_size : slv_port_axi.ar_size , - axi_cache: slv_port_axi.ar_cache - }; - - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - end - // INCR downsize - else begin - automatic int unsigned num_beats = (slv_port_axi.ar_len + 1) * conv_ratio(slv_port_axi.ar_size, AxiMstPortMaxSize) - aligned_adjustment(slv_port_axi.ar_addr, slv_port_axi.ar_size); - // One burst - if (num_beats <= 256) begin - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : num_beats - 1 , - axi_burst: slv_port_axi.ar_burst, - axi_size : AxiMstPortMaxSize , - axi_cache: slv_port_axi.ar_cache - }; - - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - end - // Need to split the incoming burst into several INCR bursts - else begin - automatic axi_addr_t burst_addr; - automatic len_t burst_len ; - - // First burst is a "partial" burst - burst_len = 255 - aligned_adjustment(slv_port_axi.ar_addr, slv_port_axi.ar_size); - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : burst_len , - axi_burst: slv_port_axi.ar_burst, - axi_size : AxiMstPortMaxSize , - axi_cache: slv_port_axi.ar_cache - } ; - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - - // Push the other bursts in a loop - num_beats = num_beats - burst_len - 1 ; - burst_addr = axi_pkg::beat_addr(axi_pkg::aligned_addr(slv_port_axi.ar_addr, AxiMstPortMaxSize), AxiMstPortMaxSize, burst_len, axi_pkg::BURST_INCR, burst_len+1); - while (num_beats != 0) begin - burst_len = num_beats >= 256 ? 255 : num_beats - 1; - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : burst_addr , - axi_len : burst_len , - axi_burst: slv_port_axi.ar_burst, - axi_size : AxiMstPortMaxSize , - axi_cache: slv_port_axi.ar_cache - } ; - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - - num_beats = num_beats - burst_len - 1 ; - burst_addr = axi_pkg::beat_addr(burst_addr, AxiMstPortMaxSize, burst_len, axi_pkg::BURST_INCR, burst_len+1); - end; - end - end - end - // Passthrough downsize - axi_pkg::BURST_FIXED: begin - // Transaction unchanged - if (conv_ratio(slv_port_axi.ar_size, AxiMstPortMaxSize) == 1) begin - exp_slv_ar = '{ - axi_id : slv_port_axi.ar_id , - axi_addr : slv_port_axi.ar_addr , - axi_len : slv_port_axi.ar_len , - axi_burst: slv_port_axi.ar_burst, - axi_size : slv_port_axi.ar_size , - axi_cache: slv_port_axi.ar_cache - }; - - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - end - // Split into master_axi.ar_len + 1 INCR bursts - else begin - for (int unsigned j = 0; j <= slv_port_axi.ar_len; j++) begin - exp_slv_ar.axi_id = slv_port_axi.ar_id ; - exp_slv_ar.axi_addr = slv_port_axi.ar_addr ; - exp_slv_ar.axi_burst = axi_pkg::BURST_INCR ; - exp_slv_ar.axi_size = AxiMstPortMaxSize ; - exp_slv_ar.axi_cache = slv_port_axi.ar_cache; - if (conv_ratio(slv_port_axi.ar_size, AxiMstPortMaxSize) >= aligned_adjustment(slv_port_axi.ar_addr, slv_port_axi.ar_size) + 1) - exp_slv_ar.axi_len = conv_ratio(slv_port_axi.ar_size, AxiMstPortMaxSize) - aligned_adjustment(slv_port_axi.ar_addr, slv_port_axi.ar_size) - 1; - else - exp_slv_ar.axi_len = 0; - - this.exp_mst_port_ar_queue.push(slv_port_axi.ar_id, exp_slv_ar); - incr_expected_tests(6) ; - end - end - end - // WRAP downsize - axi_pkg::BURST_WRAP: begin - exp_slv_ar = '0; - $warning("WRAP bursts are not supported."); - end - endcase - - $display("%0tns > Master: AR with ID: %b", - $time, slv_port_axi.ar_id); - end - endtask : mon_slv_port_ar - - virtual task automatic mon_mst_port_r(); - if (mst_port_axi.r_valid && mst_port_axi.r_ready) begin - // Retrieve the AR requests related to this R beat - exp_ax_t act_mst_ar = act_mst_port_ar_queue.get(mst_port_axi.r_id); - exp_ax_t act_slv_ar = act_slv_port_ar_queue.get(mst_port_axi.r_id); - axi_id_t id = mst_port_axi.r_id ; - - // Calculate the offsets - shortint unsigned mst_port_lower_byte = - axi_pkg::beat_lower_byte(act_mst_ar.axi_addr, act_mst_ar.axi_size, act_mst_ar.axi_len, act_mst_ar.axi_burst, AxiMstPortStrbWidth, mst_port_r_cnt[id]); - shortint unsigned mst_port_upper_byte = - axi_pkg::beat_upper_byte(act_mst_ar.axi_addr, act_mst_ar.axi_size, act_mst_ar.axi_len, act_mst_ar.axi_burst, AxiMstPortStrbWidth, mst_port_r_cnt[id]); - shortint unsigned slv_port_lower_byte = - axi_pkg::beat_lower_byte(act_slv_ar.axi_addr, act_slv_ar.axi_size, act_slv_ar.axi_len, act_slv_ar.axi_burst, AxiSlvPortStrbWidth, slv_port_r_cnt[id]); - shortint unsigned slv_port_upper_byte = - axi_pkg::beat_upper_byte(act_slv_ar.axi_addr, act_slv_ar.axi_size, act_slv_ar.axi_len, act_slv_ar.axi_burst, AxiSlvPortStrbWidth, slv_port_r_cnt[id]); - - // Pointer inside the outcoming word - shortint unsigned bytes_copied = 0; - if (slv_port_r_pnt[id] == '1) - slv_port_r_pnt[id] = slv_port_lower_byte; - - slv_port_r[id].axi_id = id ; - slv_port_r[id].axi_last = slv_port_r_cnt[id] == act_slv_ar.axi_len; - for (shortint unsigned b = mst_port_lower_byte; b <= mst_port_upper_byte; b++) begin - if (b + slv_port_r_pnt[id] - mst_port_lower_byte == AxiSlvPortStrbWidth) - break; - slv_port_r[id].axi_data[8*(b + slv_port_r_pnt[id] - mst_port_lower_byte) +: 8] = mst_port_axi.r_data[8*b +: 8]; - bytes_copied++; - end - - // Increment the len counters - mst_port_r_cnt[id]++ ; - slv_port_r_pnt[id] += bytes_copied; - - if (slv_port_r_pnt[id] == slv_port_upper_byte + 1 // Used all bits from the incoming R beat - || slv_port_r_pnt[id] == AxiSlvPortStrbWidth // Filled up an outcoming R beat - || conv_ratio(act_slv_ar.axi_size, act_mst_ar.axi_size) == 1 // Not downsizing - || mst_port_axi.r_last // Last beat of an R burst - ) begin - // Don't care for the bits outside these accessed by this request - for (int unsigned b = 0; b < AxiSlvPortStrbWidth; b++) - if (!(slv_port_lower_byte <= b && b <= slv_port_upper_byte)) - slv_port_r[id].axi_data[8*b +: 8] = {8{1'b?}}; - - this.exp_slv_port_r_queue.push(id, slv_port_r[id]); - incr_expected_tests(3) ; - - // Increment the len counter - slv_port_r_cnt[id]++; - - // Reset R beat - slv_port_r[id] = '0; - slv_port_r_pnt[id] = '1; - end - - // Pop the AW request from the queues - if (slv_port_r_cnt[id] == act_slv_ar.axi_len + 1) begin - void'(act_slv_port_ar_queue.pop_id(id)); - slv_port_r_cnt[id] = 0; - end - if (mst_port_r_cnt[id] == act_mst_ar.axi_len + 1) begin - void'(act_mst_port_ar_queue.pop_id(id)); - mst_port_r_cnt[id] = 0; - end - end - endtask : mon_mst_port_r - endclass : axi_dw_downsizer_monitor - -endpackage: tb_axi_dw_pkg diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_dw_upsizer.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_dw_upsizer.sv deleted file mode 100644 index 26f11f048..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_dw_upsizer.sv +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Matheus Cavalcante - -`include "axi/assign.svh" - -module tb_axi_dw_upsizer #( - // AXI Parameters - parameter int unsigned TbAxiAddrWidth = 64 , - parameter int unsigned TbAxiIdWidth = 4 , - parameter int unsigned TbAxiSlvPortDataWidth = 32 , - parameter int unsigned TbAxiMstPortDataWidth = 64 , - parameter int unsigned TbAxiUserWidth = 8 , - // TB Parameters - parameter time TbCyclTime = 10ns, - parameter time TbApplTime = 2ns , - parameter time TbTestTime = 8ns - ); - - /********************* - * CLOCK GENERATOR * - *********************/ - - logic clk; - logic rst_n; - logic eos; - - clk_rst_gen #( - .ClkPeriod (TbCyclTime), - .RstClkCycles (5 ) - ) i_clk_rst_gen ( - .clk_o (clk ), - .rst_no(rst_n) - ); - - /********* - * AXI * - *********/ - - // Master port - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiSlvPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) master_dv ( - .clk_i(clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiSlvPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) master (); - - `AXI_ASSIGN(master, master_dv) - - axi_test::axi_rand_master #( - .AW (TbAxiAddrWidth ), - .DW (TbAxiSlvPortDataWidth), - .IW (TbAxiIdWidth ), - .UW (TbAxiUserWidth ), - .TA (TbApplTime ), - .TT (TbTestTime ), - .MAX_READ_TXNS (8 ), - .MAX_WRITE_TXNS(8 ), - .AXI_ATOPS (1'b1 ) - ) master_drv = new (master_dv); - - // Slave port - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiMstPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) slave_dv ( - .clk_i(clk) - ); - - AXI_BUS #( - .AXI_ADDR_WIDTH(TbAxiAddrWidth ), - .AXI_DATA_WIDTH(TbAxiMstPortDataWidth), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_USER_WIDTH(TbAxiUserWidth ) - ) slave (); - - axi_test::axi_rand_slave #( - .AW(TbAxiAddrWidth ), - .DW(TbAxiMstPortDataWidth), - .IW(TbAxiIdWidth ), - .UW(TbAxiUserWidth ), - .TA(TbApplTime ), - .TT(TbTestTime ) - ) slave_drv = new (slave_dv); - - `AXI_ASSIGN(slave_dv, slave) - - /********* - * DUT * - *********/ - - axi_dw_converter_intf #( - .AXI_MAX_READS (4 ), - .AXI_ADDR_WIDTH (TbAxiAddrWidth ), - .AXI_ID_WIDTH (TbAxiIdWidth ), - .AXI_SLV_PORT_DATA_WIDTH(TbAxiSlvPortDataWidth), - .AXI_MST_PORT_DATA_WIDTH(TbAxiMstPortDataWidth), - .AXI_USER_WIDTH (TbAxiUserWidth ) - ) i_dw_converter ( - .clk_i (clk ), - .rst_ni(rst_n ), - .slv (master), - .mst (slave ) - ); - - /************* - * DRIVERS * - *************/ - - initial begin - eos = 1'b0; - - // Configuration - slave_drv.reset() ; - master_drv.reset() ; - master_drv.add_memory_region({TbAxiAddrWidth{1'b0}}, {TbAxiAddrWidth{1'b1}}, axi_pkg::WTHRU_NOALLOCATE); - - // Wait for the reset before sending requests - @(posedge rst_n); - - fork - // Act as a sink - slave_drv.run() ; - master_drv.run(200, 200); - join_any - - // Done - repeat (10) @(posedge clk); - eos = 1'b1; - end - - /************* - * MONITOR * - *************/ - - initial begin : proc_monitor - static tb_axi_dw_pkg::axi_dw_upsizer_monitor #( - .AxiAddrWidth (TbAxiAddrWidth ), - .AxiMstPortDataWidth(TbAxiMstPortDataWidth), - .AxiSlvPortDataWidth(TbAxiSlvPortDataWidth), - .AxiIdWidth (TbAxiIdWidth ), - .AxiUserWidth (TbAxiUserWidth ), - .TimeTest (TbTestTime ) - ) monitor = new (master_dv, slave_dv); - fork - monitor.run(); - forever begin - #TbTestTime; - if(eos) begin - monitor.print_result(); - $stop() ; - end - @(posedge clk); - end - join - end - -// vsim -voptargs=+acc work.tb_axi_dw_upsizer -endmodule : tb_axi_dw_upsizer diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_isolate.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_isolate.sv deleted file mode 100644 index e148afbd0..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_isolate.sv +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger -// - Andreas Kurth - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_isolate #( - parameter int unsigned NoWrites = 50000, // How many writes per master - parameter int unsigned NoReads = 30000 // How many reads per master - ); - // Random master no Transactions - localparam int unsigned NoPendingDut = 16; - // Random Master Atomics - localparam int unsigned MaxAW = 32'd30; - localparam int unsigned MaxAR = 32'd30; - localparam bit EnAtop = 1'b1; - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - // AXI configuration - localparam int unsigned AxiIdWidth = 4; - localparam int unsigned AxiAddrWidth = 32; // Axi Address Width - localparam int unsigned AxiDataWidth = 64; // Axi Data Width - localparam int unsigned AxiUserWidth = 5; - // Sim print config, how many transactions - localparam int unsigned PrintTnx = 1000; - - - typedef axi_test::axi_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidth ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( MaxAR ), - .MAX_WRITE_TXNS ( MaxAW ), - .AXI_ATOPS ( EnAtop ) - ) axi_rand_master_t; - typedef axi_test::axi_rand_slave #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidth ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) - ) axi_rand_slave_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - logic rst_n; - logic end_of_sim; - // DUT signals - logic isolate, isolated; - - // interfaces - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv (clk); - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv (clk); - - `AXI_ASSIGN ( master, master_dv ) - `AXI_ASSIGN ( slave_dv, slave ) - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_isolate_intf #( - .NUM_PENDING ( NoPendingDut ), // number of pending requests - .AXI_ID_WIDTH ( AxiIdWidth ), // AXI ID width - .AXI_ADDR_WIDTH ( AxiAddrWidth ), // AXI address width - .AXI_DATA_WIDTH ( AxiDataWidth ), // AXI data width - .AXI_USER_WIDTH ( AxiUserWidth ) // AXI user width - ) i_dut ( - .clk_i ( clk ), // clock - .rst_ni ( rst_n ), // asynchronous reset active low - .slv ( master ), // slave port - .mst ( slave ), // master port - .isolate_i ( isolate ), // isolate master port from slave port - .isolated_o ( isolated ) // master port is isolated from slave port - ); - - initial begin : proc_axi_master - automatic axi_rand_master_t axi_rand_master = new(master_dv); - end_of_sim <= 1'b0; - axi_rand_master.add_memory_region(32'h0000_0000, 32'h1000_0000, axi_pkg::DEVICE_NONBUFFERABLE); - axi_rand_master.add_memory_region(32'h2000_0000, 32'h3000_0000, axi_pkg::WTHRU_NOALLOCATE); - axi_rand_master.add_memory_region(32'h4000_0000, 32'h5000_0000, axi_pkg::WBACK_RWALLOCATE); - axi_rand_master.reset(); - @(posedge rst_n); - axi_rand_master.run(NoReads, NoWrites); - end_of_sim <= 1'b1; - repeat (10000) @(posedge clk); - $stop(); - end - - initial begin : proc_axi_slave - automatic axi_rand_slave_t axi_rand_slave = new(slave_dv); - axi_rand_slave.reset(); - @(posedge rst_n); - axi_rand_slave.run(); - end - - initial begin : proc_sim_ctl - forever begin - isolate <= 1'b0; - repeat ($urandom_range(100000,1)) @(posedge clk); - isolate <= 1'b1; - repeat ($urandom_range(100000,1)) @(posedge clk); - end - end - - initial begin : proc_sim_progress - automatic int unsigned aw = 0; - automatic int unsigned ar = 0; - automatic bit aw_printed = 1'b0; - automatic bit ar_printed = 1'b0; - - @(posedge rst_n); - - forever begin - @(posedge clk); - #TestTime; - if (master.aw_valid && master.aw_ready) begin - aw++; - end - if (master.ar_valid && master.ar_ready) begin - ar++; - end - - if ((aw % PrintTnx == 0) && ! aw_printed) begin - $display("%t> Transmit AW %d of %d.", $time(), aw, NoWrites); - aw_printed = 1'b1; - end - if ((ar % PrintTnx == 0) && !ar_printed) begin - $display("%t> Transmit AR %d of %d.", $time(), ar, NoReads); - ar_printed = 1'b1; - end - - if (aw % PrintTnx == 1) begin - aw_printed = 1'b0; - end - if (ar % PrintTnx == 1) begin - ar_printed = 1'b0; - end - - if (end_of_sim) begin - $info("All transactions completed."); - break; - end - end - end - - - default disable iff (!rst_n); - aw_unstable: assert property (@(posedge clk) - (slave.aw_valid && !slave.aw_ready) |=> $stable(slave.aw_addr)) else - $fatal(1, "AW is unstable."); - w_unstable: assert property (@(posedge clk) - (slave.w_valid && !slave.w_ready) |=> $stable(slave.w_data)) else - $fatal(1, "W is unstable."); - b_unstable: assert property (@(posedge clk) - (master.b_valid && !master.b_ready) |=> $stable(master.b_resp)) else - $fatal(1, "B is unstable."); - ar_unstable: assert property (@(posedge clk) - (slave.ar_valid && !slave.ar_ready) |=> $stable(slave.ar_addr)) else - $fatal(1, "AR is unstable."); - r_unstable: assert property (@(posedge clk) - (master.r_valid && !master.r_ready) |=> $stable(master.r_data)) else - $fatal(1, "R is unstable."); - - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_mailbox.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_lite_mailbox.sv deleted file mode 100644 index 255702922..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_mailbox.sv +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright (c) 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger - -// Directed Verification Testbench for `axi_lite_mailbox`. -// - On port 0 all registers get read and the expected results asserted. -// - Read from an empty read FIFO on Port 0 and clear its interrupt -// - Request some data from the other port. -// - Port 0 sends one data_t to the other and sets its read threshold and waits until -// port 1 fills the FIFO to a certain value. Port one waits for its interrupt, sets a -// write threshold and fills the FIFO, until it reaches it. While the FIFO is filling, -// port 0 starts emptying the read FIFO, however is slower than port 1 pushing, as port -// 0 checks if the FIFO is empty. Port 1 stops sending, when the interrupt for its write -// threshold is recieved. Port 0 empties the FIFO completely and resets its read interrupt. -// - Port 0 flushes both FIFOs -// - Port 0 enables error interrupt and pushes data until the FIFO errors, then clears it by -// flushing the FIFOs and reading the `ERROR` register. -// - Port 0 makes an unmapped read and write access, and some targeted accesses to get the branch -// coverage to 96.25%, the not taken branches have to do with the stalling -// capabilities of the slave, when the response path is not ready. -// Each of these tests has the respective AXI Lite transaction asserted in the expected output. -// Simulation end tells the number of failed assertions. - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_lite_mailbox; - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - // axi configuration - localparam int unsigned AxiAddrWidth = 32'd32; // Axi Address Width - localparam int unsigned AxiDataWidth = 32'd64; // Axi Data Width - - // mailbox params - localparam int unsigned MailboxDepth = 32'd16; - - typedef logic [AxiAddrWidth-1:0] addr_t; // for sign extension - typedef logic [AxiDataWidth-1:0] data_t; // for sign extension - - typedef enum addr_t { - MBOXW = addr_t'(0 * AxiDataWidth/8), - MBOXR = addr_t'(1 * AxiDataWidth/8), - STATUS = addr_t'(2 * AxiDataWidth/8), - ERROR = addr_t'(3 * AxiDataWidth/8), - WIRQT = addr_t'(4 * AxiDataWidth/8), - RIRQT = addr_t'(5 * AxiDataWidth/8), - IRQS = addr_t'(6 * AxiDataWidth/8), - IRQEN = addr_t'(7 * AxiDataWidth/8), - IRQP = addr_t'(8 * AxiDataWidth/8), - CTRL = addr_t'(9 * AxiDataWidth/8) - } reg_addr_e; - - typedef axi_test::axi_lite_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - .MIN_ADDR ( 32'h0000_0000 ), - .MAX_ADDR ( 32'h0001_3000 ), - .MAX_READ_TXNS ( 10 ), - .MAX_WRITE_TXNS ( 10 ) - ) rand_lite_master_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - // DUT signals - logic rst_n; - logic [1:0] end_of_sim; - - logic [1:0] irq; - - int unsigned test_failed [1:0]; - - // ------------------------------- - // AXI Interfaces - // ------------------------------- - AXI_LITE #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master [1:0] (); - AXI_LITE_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master_dv [1:0] (clk); - for (genvar i = 0; i < 2; i++) begin : gen_conn_dv_masters - `AXI_LITE_ASSIGN(master[i], master_dv[i]) - end - - // Masters control simulation run time - initial begin : proc_master_0 - automatic rand_lite_master_t lite_axi_master = new ( master_dv[0], "MST_0"); - automatic data_t data = '0; - automatic axi_pkg::resp_t resp = axi_pkg::RESP_SLVERR; - // automatic int unsigned test_failed[0] = 0; - automatic int unsigned loop = 0; - end_of_sim[0] <= 1'b0; - lite_axi_master.reset(); - @(posedge rst_n); - - // ------------------------------- - // Read all registers anf compare their results - // ------------------------------- - $info("Initial test by reading each register"); - $display("%0t MST_0> Read register MBOXW ", $time()); - lite_axi_master.read(MBOXW, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(32'hFEEDC0DE)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register MBOXR, this generates an error ", $time()); - lite_axi_master.read(MBOXR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(32'hFEEDDEAD)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_SLVERR) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register STATUS", $time()); - lite_axi_master.read(STATUS, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(1)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register ERROR ", $time()); - lite_axi_master.read(ERROR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(1)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register WIRQT ", $time()); - lite_axi_master.read(WIRQT, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(0)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register RIRQT ", $time()); - lite_axi_master.read(RIRQT, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(0)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register IRQS ", $time()); - lite_axi_master.read(IRQS, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(3'b100)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Acknowledge Error by writing to IRQS", $time()); - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), 64'h4, 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register IRQEN ", $time()); - lite_axi_master.read(IRQEN, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(0)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register IRQP ", $time()); - lite_axi_master.read(IRQP, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(0)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register CTRL ", $time()); - lite_axi_master.read(CTRL, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(0)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - // ------------------------------- - // Test Read error interrupt on port 0 - // ------------------------------- - repeat (50) @(posedge clk); - $info("Test error interrupt"); - $display("%0t MST_0> Enable Error interrupt ", $time()); - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(3'b100), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register MBOXR, this generates an error ", $time()); - lite_axi_master.read(MBOXR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(32'hFEEDDEAD)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_SLVERR) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read register ERROR ", $time()); - lite_axi_master.read(ERROR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(1)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Acknowledge Error by writing to IRQS", $time()); - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), data_t'(3'b100), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Disable interrupt", $time()); - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(0), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - // ------------------------------- - // Send data to the other port, and enable interrupt for recieving - // ------------------------------- - repeat (50) @(posedge clk); - $info("Test sending data from one to the other slave interface"); - $display("%0t MST_0> Set write threshold to 100, truncates to depth ", $time()); - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), 64'd100, 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read out write threshold ", $time()); - lite_axi_master.read(WIRQT, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(MailboxDepth - 1)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Set write threshold to 0", $time()); - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), 64'd0, 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - - $display("%0t MST_0> Set Read threshold to 100, truncates to depth ", $time()); - lite_axi_master.write(RIRQT, axi_pkg::prot_t'('0), 64'd100, 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Read out read threshold ", $time()); - lite_axi_master.read(RIRQT, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(MailboxDepth - 1)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Set Read threshold to 64'd2 ", $time()); - lite_axi_master.write(RIRQT, axi_pkg::prot_t'('0), 64'd2, 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Enable Read threshold interrupt ", $time()); - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), 64'h2, 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - $display("%0t MST_0> Send to slave 1 data ", $time()); - lite_axi_master.write(MBOXW, axi_pkg::prot_t'('0), data_t'(32'hFEEDFEED), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - // wait for interrupt - wait (irq[0]); - $display("%0t MST_0> interrupt recieved, test that it is the expected one ", $time()); - lite_axi_master.read(IRQP, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(3'b010)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.read(STATUS, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(4'b1000)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - $display("%0t MST_0> empty data from port ", $time()); - while (!data[0]) begin - lite_axi_master.read(MBOXR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(loop)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - loop ++; - lite_axi_master.read(STATUS, axi_pkg::prot_t'('0), data, resp); - end - $display("%0t MST_0> FIFO is now empty, clear interrupt and disable it ", $time()); - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), data_t'(3'b111), 8'hFF, resp); - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(0), 8'hFF, resp); - lite_axi_master.write(RIRQT, axi_pkg::prot_t'('0), data_t'(0), 8'hFF, resp); - - // ------------------------------- - // Test Flush - // ------------------------------- - repeat (50) @(posedge clk); - $info("%0t MST_0> Test Flush all FIFOs ", $time()); - lite_axi_master.write(CTRL, axi_pkg::prot_t'('0), data_t'('1), 8'h00, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - lite_axi_master.write(CTRL, axi_pkg::prot_t'('0), data_t'('1), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - // ------------------------------- - // Fill the write FIFO, until write error interrupt, then flush and clear interrupt - // ------------------------------- - repeat (50) @(posedge clk); - $info("%0t MST_0> Test Write error interrupt ", $time()); - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(3'b100), 8'hFF, resp); - loop = 0; - while (!irq[0]) begin - lite_axi_master.write(MBOXW, axi_pkg::prot_t'('0), data_t'(loop), 8'hFF, resp); - end - lite_axi_master.read(IRQP, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(3'b100)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.read(ERROR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(2'b10)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(CTRL, axi_pkg::prot_t'('0), data_t'(2'b01), 8'h01, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), data_t'(3'b111), 8'h01, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.read(STATUS, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(4'b0001)) else begin test_failed[0]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - // ------------------------------- - // Make an unmapped read and write access - // ------------------------------- - repeat (50) @(posedge clk); - $info("%0t MST_0> Make an unmapped access read and write ", $time()); - lite_axi_master.read(addr_t'(16'hDEAD), axi_pkg::prot_t'('0), data, resp); - assert (resp == axi_pkg::RESP_SLVERR) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(addr_t'(16'hDEAD), axi_pkg::prot_t'('0), data_t'(16'hDEAD), 8'hFF, resp); - assert (resp == axi_pkg::RESP_SLVERR) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(ERROR, axi_pkg::prot_t'('0), data_t'(16'hDEAD), 8'hFF, resp); - assert (resp == axi_pkg::RESP_SLVERR) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(16'hDEAD), 8'h00, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), data_t'(16'hDEAD), 8'h00, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), data_t'('1), 8'h00, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), data_t'('1), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), data_t'('0), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(RIRQT, axi_pkg::prot_t'('0), data_t'('1), 8'h00, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(RIRQT, axi_pkg::prot_t'('0), data_t'('1), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), data_t'('0), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[0]++; $error("Unexpected result"); end - - end_of_sim[0] <= 1'b1; - end - - initial begin : proc_master_1 - automatic rand_lite_master_t lite_axi_master = new ( master_dv[1], "MST_1"); - //automatic int unsigned test_failed = 0; - automatic data_t data = '0; - automatic axi_pkg::resp_t resp = axi_pkg::RESP_SLVERR; - automatic int unsigned loop = 0; - end_of_sim[1] <= 1'b0; - lite_axi_master.reset(); - @(posedge rst_n); - - // ------------------------------- - // Test Flush seperately - // ------------------------------- - $display("%0t MST_1> Flush Read MBOX ", $time()); - lite_axi_master.write(CTRL, axi_pkg::prot_t'('0), data_t'(2'b10), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - $display("%0t MST_1> Flush Write MBOX ", $time()); - lite_axi_master.write(CTRL, axi_pkg::prot_t'('0), data_t'(2'b01), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - // ------------------------------- - // Set read and write thresholds, wait for reflect some data - // ------------------------------- - $display("%0t MST_1> Set Read threshold to 64'd0 ", $time()); - lite_axi_master.write(RIRQT, axi_pkg::prot_t'('0), data_t'(0), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - $display("%0t MST_1> Enable Read threshold interrupt ", $time()); - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(2), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - $display("%0t MST_1> Wait for Read threshold interrupt ", $time()); - wait (irq[1]); - $display("%0t MST_1> Interrupt Recieved, read pending register and Acknowledge irq ", $time()); - lite_axi_master.read(IRQP, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(2)) else begin test_failed[1]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - lite_axi_master.read(IRQS, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(2)) else begin test_failed[1]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - lite_axi_master.read(MBOXR, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(32'hFEEDFEED)) else begin test_failed[1]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), data_t'(2), 8'h1, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - $display("%0t MST_1> Enable write threshold interrupt ", $time()); - lite_axi_master.write(WIRQT, axi_pkg::prot_t'('0), 32'h8, 8'h1, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(1), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - $display("%0t MST_1> Write back looping answer ", $time()); - while (!irq[1]) begin - lite_axi_master.write(MBOXW, axi_pkg::prot_t'('0), data_t'(loop), 8'hFF, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - loop++; - end - $display("%0t MST_1> Stop looping answer and clear interrupt", $time()); - lite_axi_master.read(IRQP, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(1)) else begin test_failed[1]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - lite_axi_master.read(IRQS, axi_pkg::prot_t'('0), data, resp); - assert (data == data_t'(1)) else begin test_failed[1]++; $error("Unexpected result"); end - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - // clear the interrupt, if the Write FIFO is status reg is below threshold - lite_axi_master.read(STATUS, axi_pkg::prot_t'('0), data, resp); - while (data[3]) begin - repeat (10) @(posedge clk); - lite_axi_master.read(STATUS, axi_pkg::prot_t'('0), data, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - end - lite_axi_master.write(IRQS, axi_pkg::prot_t'('0), data_t'(2), 8'h1, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - lite_axi_master.write(IRQEN, axi_pkg::prot_t'('0), data_t'(0), 8'h1, resp); - assert (resp == axi_pkg::RESP_OKAY) else begin test_failed[1]++; $error("Unexpected result"); end - - end_of_sim[1] <= 1'b1; - end - - initial begin : proc_monitor_irq_0 - forever begin - @(posedge irq[0]); - $info("Recieved interrupt from slave port 0"); - end - end - - initial begin : proc_monitor_irq_1 - forever begin - @(posedge irq[1]); - $info("Recieved interrupt from slave port 1"); - end - end - - initial begin : proc_stop_sim - wait (&end_of_sim); - repeat (50) @(posedge clk); - $display("Slave port 0 failed tests: %0d", test_failed[0]); - $display("Slave port 1 failed tests: %0d", test_failed[1]); - if (test_failed[0] > 0 || test_failed[1] > 0) begin - $fatal(1, "Simulation stopped as assertion errors have been encountered, Failure!!!"); - end else begin - $info("Simulation stopped as all Masters transferred their data, Success.",); - end - $stop(); - end - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_lite_mailbox_intf #( - .MAILBOX_DEPTH ( MailboxDepth ), - .IRQ_EDGE_TRIG ( 1'b0 ), - .IRQ_ACT_HIGH ( 1'b1 ), - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) i_mailbox_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_i ( 1'b0 ), - .slv ( master ), - .irq_o ( irq ), - .base_addr_i ( '0 ) // set base address to '0 - ); -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_regs.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_lite_regs.sv deleted file mode 100644 index 0b90bcac3..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_regs.sv +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (c) 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger - -// Directed Random Verification Testbench for `axi_lite_regs`. - -`include "axi/assign.svh" - -/// Testbench for validating the module `axi_lite_regs`. -module tb_axi_lite_regs #( - /// Define the parameter `RegNumBytes` of the DUT. - parameter int unsigned TbRegNumBytes = 32'd200, - /// Define the parameter `AxiReadOnly` of the DUT. - parameter logic [TbRegNumBytes-1:0] TbAxiReadOnly = {{TbRegNumBytes-18{1'b0}}, 18'b101110111111000000}, - /// Define the parameter `PrivProtOnly` of the DUT. - parameter bit TbPrivProtOnly = 1'b0, - /// Define the parameter `SecuProtOnly` of the DUT. - parameter bit TbSecuProtOnly = 1'b0, - /// Number of random writes generated by the testbench AXI4-Lite random master. - parameter int unsigned TbNoWrites = 32'd1000, - /// Number of random reads generated by the testbench AXI4-Lite random master. - parameter int unsigned TbNoReads = 32'd1500 -); - // AXI configuration - localparam int unsigned AxiAddrWidth = 32'd32; // Axi Address Width - localparam int unsigned AxiDataWidth = 32'd32; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 32'd8; - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - - typedef logic [7:0] byte_t; - typedef logic [AxiAddrWidth-1:0] axi_addr_t; - typedef logic [AxiDataWidth-1:0] axi_data_t; - typedef logic [AxiStrbWidth-1:0] axi_strb_t; - - localparam axi_addr_t StartAddr = axi_addr_t'(0); - localparam axi_addr_t EndAddr = - axi_addr_t'(StartAddr + TbRegNumBytes + TbRegNumBytes/5); - - localparam byte_t [TbRegNumBytes-1:0] RegRstVal = '0; - - typedef axi_test::axi_lite_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - .MIN_ADDR ( StartAddr ), - .MAX_ADDR ( EndAddr ), - .MAX_READ_TXNS ( 10 ), - .MAX_WRITE_TXNS ( 10 ) - ) rand_lite_master_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - // DUT signals - logic rst_n; - logic end_of_sim; - // Register signals - byte_t [TbRegNumBytes-1:0] reg_d, reg_q; - logic [TbRegNumBytes-1:0] wr_active, rd_active, reg_load; - - // ------------------------------- - // AXI Interfaces - // ------------------------------- - AXI_LITE #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master (); - AXI_LITE_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master_dv (clk); - `AXI_LITE_ASSIGN(master, master_dv) - - // ------------------------------- - // AXI Rand Masters and Slaves - // ------------------------------- - // Masters control simulation run time - initial begin : proc_generate_axi_traffic - automatic rand_lite_master_t lite_axi_master = new ( master_dv, "Lite Master"); - automatic axi_data_t data = '0; - automatic axi_pkg::resp_t resp = '0; - end_of_sim <= 1'b0; - lite_axi_master.reset(); - @(posedge rst_n); - repeat (5) @(posedge clk); - // Test known register. - // Write to it. - lite_axi_master.write(axi_addr_t'(32'h0000_0000), axi_pkg::prot_t'('0), - axi_data_t'(64'hDEADBEEFDEADBEEF), axi_strb_t'(8'hFF), resp); - if (TbPrivProtOnly || TbSecuProtOnly) begin - assert (resp == axi_pkg::RESP_SLVERR) else - $fatal(1, "PrivProtOnly || SecuProtOnly: Unprivileged access was falsely granted."); - end else begin - assert (resp == axi_pkg::RESP_OKAY) else - $fatal(1, "Access should be granted"); - end - // Read from it. - lite_axi_master.read(axi_addr_t'(32'h0000_0000), axi_pkg::prot_t'('0), data, resp); - if (TbPrivProtOnly || TbSecuProtOnly) begin - // Expect error response - assert (resp == axi_pkg::RESP_SLVERR) else - $fatal(1, "PrivProtOnly || SecuProtOnly: Unprivileged access was falsely granted."); - assert (data == axi_data_t'(32'hBA5E1E55)) else - $fatal(1, "Data is unexpected, should be axi_data_t'(32'hBA5E1E55)."); - end else begin - assert (resp == axi_pkg::RESP_OKAY) else - $fatal(1, "Access should be granted."); - // Checking of the expecetd read data is handled in `proc_check_read_data`. - end - - // Let random stimuli application checking is separate. - lite_axi_master.run(TbNoReads, TbNoWrites); - end_of_sim <= 1'b1; - end - - initial begin : proc_generate_direct_load - for (int unsigned i = 0; i < TbRegNumBytes; i++) begin - automatic int unsigned j = i; - fork - toggle_load(j); - join_none - end - end - - task automatic toggle_load(int unsigned byte_i); - byte_t rand_val; - int unsigned rand_wait; - @(posedge rst_n); - reg_load[byte_i] = 1'b0; - reg_d[byte_i] = 8'h00; - @(posedge clk); - forever begin - rand_wait = $urandom_range(0, 20); - repeat (rand_wait) @(posedge clk); - rand_val = byte_t'($urandom()); - reg_d[byte_i] <= #ApplTime rand_val; - reg_load[byte_i] <= #ApplTime 1'b1; - @(posedge clk); - reg_d[byte_i] <= #ApplTime '0; - reg_load[byte_i] <= #ApplTime 1'b0; - end - endtask : toggle_load - - initial begin : proc_check_read_data - automatic axi_data_t exp_rdata[$]; - automatic axi_pkg::resp_t exp_resp[$]; - - @(posedge rst_n); - forever begin - @(posedge clk); - #(TestTime); - - // Push the expected data back. - if (master.ar_valid && master.ar_ready) begin - automatic int unsigned ar_idx = ((master.ar_addr - StartAddr) - >> $clog2(AxiStrbWidth) << $clog2(AxiStrbWidth)); - automatic axi_data_t r_data = axi_data_t'(0); - automatic axi_pkg::resp_t r_resp = axi_pkg::RESP_SLVERR; - for (int unsigned i = 0; i < AxiStrbWidth; i++) begin - if ((ar_idx+i) < TbRegNumBytes) begin - r_data[8*i+:8] = reg_q[ar_idx+i]; - r_resp = axi_pkg::RESP_OKAY; - end else begin - r_data[8*i+:8] = 8'hxx; - end - end - // check the right protection access - if (TbPrivProtOnly && !master.ar_prot[0]) begin - r_resp = axi_pkg::RESP_SLVERR; - end - if (TbSecuProtOnly && !master.ar_prot[1]) begin - r_resp = axi_pkg::RESP_SLVERR; - end - exp_resp.push_back(r_resp); - exp_rdata.push_back(r_data); - end - - // compare data if there is a value expected - if (master.r_valid && master.r_ready) begin - automatic axi_data_t r_data = exp_rdata.pop_front(); - if (master.r_resp == axi_pkg::RESP_OKAY) begin - for (int unsigned i = 0; i < AxiStrbWidth; i++) begin - automatic byte_t exp_byte = r_data[8*i+:8]; - if (exp_byte !== 8'hxx) begin - assert (master.r_data[8*i+:8] == exp_byte) else - $error("Unexpected read data: exp: %0h observed: %0h", r_data, master.r_data); - assert (master.r_resp == axi_pkg::RESP_OKAY); - end - end - end else if (master.r_resp == axi_pkg::RESP_SLVERR) begin - assert (master.r_data == axi_data_t'(32'hBA5E1E55)); - end else begin - $error("Slave responded with false response: %0h", master.r_resp); - end - end - end - end - - axi_pkg::resp_t b_resp_queue[$]; - - // Only works as module expects both AW & W valid before it does something - initial begin : proc_check_write_data - @(posedge rst_n); - forever begin - #TestTime; - // AW and W is launched, setup the test tasks - if (master.aw_valid && master.aw_ready && master.w_valid && master.w_ready) begin - automatic int unsigned aw_idx = ((master.aw_addr - StartAddr) - >> $clog2(AxiStrbWidth) << $clog2(AxiStrbWidth)); - automatic axi_pkg::resp_t exp_b_resp = (aw_idx < TbRegNumBytes) ? - axi_pkg::RESP_OKAY : axi_pkg::RESP_SLVERR; - automatic bit all_ro = 1'b1; - // check for errors from wrong access protection - if (TbPrivProtOnly && !master.aw_prot[0]) begin - exp_b_resp = axi_pkg::RESP_SLVERR; - end - if (TbSecuProtOnly && !master.aw_prot[1]) begin - exp_b_resp = axi_pkg::RESP_SLVERR; - end - // Check if all accesses bytes are read only - for (int unsigned i = 0; i < AxiStrbWidth; i++) begin - if (!TbAxiReadOnly[aw_idx+i]) begin - all_ro = 1'b0; - end - end - if (all_ro) begin - exp_b_resp = axi_pkg::RESP_SLVERR; - end - - // do the actual write checking - if (exp_b_resp == axi_pkg::RESP_OKAY) begin - // go through every byte - for (int unsigned i = 0; i < AxiStrbWidth; i++) begin - if ((aw_idx+i) < TbRegNumBytes) begin - if (master.w_strb[i]) begin - automatic int unsigned j = aw_idx + i; - automatic byte_t exp_byte = master.w_data[8*i+:8]; - fork - check_q(j, exp_byte); - join_none - end - assert (master.w_strb[i] == wr_active[aw_idx+i]); - end - end - end - b_resp_queue.push_back(exp_b_resp); - end - @(posedge clk); - end - end - - initial begin : proc_check_b - automatic axi_pkg::resp_t exp_b_resp; - @(posedge rst_n); - forever begin - #TestTime; - if (master.b_valid && master.b_ready) begin - if (b_resp_queue.size()) begin - exp_b_resp = b_resp_queue.pop_front(); - assert (exp_b_resp == master.b_resp) else - $error("Unexpected B response: EXP: %b ACT: %b", exp_b_resp, master.b_resp); - end else begin - $error("B response even no AW was sent."); - end - end - @(posedge clk); - end - end - - // check that the expected register byte has been written - task automatic check_q(int unsigned byte_i, byte_t exp_byte); - automatic byte_t save_byte = (reg_load[byte_i]) ? reg_d[byte_i] : reg_q[byte_i]; - @(posedge clk); - #TestTime; - if (!TbAxiReadOnly[byte_i]) begin - assert(exp_byte == reg_q[byte_i]) else - $error("AXI write was not registered at byte: %0d", byte_i); - end else begin - assert (reg_q[byte_i] == save_byte) else - $error("AXI write onto read only byte: %0d SAVE: %h EXP: %h ACT %h", - byte_i, save_byte, exp_byte, reg_q[byte_i]); - end - endtask : check_q - - // Some assertions for additional checking. - default disable iff (~rst_n); - for (genvar i = 0; i < TbRegNumBytes; i++) begin : gen_check_ro_bytes - if (TbAxiReadOnly[i]) begin : gen_check_ro - ro_assert_no_load: assert property (@(posedge clk) - ((wr_active[i] && !reg_load[i]) |=> $stable(reg_q[i]))) else - $fatal(1, "ReadOnly byte %0d changed from AXI write, while not direct loaded.", i); - ro_assert_load: assert property (@(posedge clk) - ((wr_active[i] && reg_load[i]) |=> (reg_q[i] === $past(reg_d[i])))) else - $fatal(1, "ReadOnly byte %0d changed from AXI write, while direct loaded.", i); - end - load_assert_no_axi: assert property (@(posedge clk) - (reg_load[i] && !TbAxiReadOnly[i] |-> !wr_active[i])) else - $fatal(1, "Byte %0d, AXI write onto non read-only and direct load are both active!", i); - load_assert_data: assert property (@(posedge clk) - (reg_load[i] |=> reg_q[i] === $past(reg_d[i]))) else - $fatal(1, "Byte %0d, direct load was executed falsely.", i); - stable_assert: assert property (@(posedge clk) - (!reg_load[i] && !wr_active[i]) |=> $stable(reg_q[i])) else - $fatal(1, "Byte %0d is unstable, when no AXI write or direct load.", i); - end - - initial begin : proc_stop_sim - wait (end_of_sim); - repeat (1000) @(posedge clk); - $display("Simulation stopped as Master transferred its data."); - $stop(); - end - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_lite_regs_intf #( - .REG_NUM_BYTES ( TbRegNumBytes ), - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .PRIV_PROT_ONLY ( TbPrivProtOnly ), - .SECU_PROT_ONLY ( TbSecuProtOnly ), - .AXI_READ_ONLY ( TbAxiReadOnly ), - .REG_RST_VAL ( RegRstVal ) - ) i_axi_lite_regs ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .slv ( master ), - .wr_active_o ( wr_active ), - .rd_active_o ( rd_active ), - .reg_d_i ( reg_d ), - .reg_load_i ( reg_load ), - .reg_q_o ( reg_q ) - ); -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_to_apb.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_lite_to_apb.sv deleted file mode 100644 index 8910198e4..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_to_apb.sv +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger -// - Andreas Kurth - -// Description: This testbench sends random generated AXI4-Lite transfers to the bridge -// Each APB slave is simulated by randomly updating its response signals each clock -// Cycle. There are some assertions testing sequences for correct APB4 signaling. - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_lite_to_apb #( - parameter bit TbPipelineRequest = 1'b0, - parameter bit TbPipelineResponse = 1'b0 -); - // Dut parameters - localparam int unsigned NoApbSlaves = 8; // How many APB Slaves there are - localparam int unsigned NoAddrRules = 9; // How many address rules for the APB slaves - // Random master no Transactions - localparam int unsigned NoWrites = 10000; // How many rand writes of the master - localparam int unsigned NoReads = 20000; // How many rand reads of the master - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - // Type widths - localparam int unsigned AxiAddrWidth = 32; - localparam int unsigned AxiDataWidth = 32; - localparam int unsigned AxiStrbWidth = AxiDataWidth/8; - - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - - `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) - `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) - `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) - - `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) - `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - - `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - - typedef logic [NoApbSlaves-1:0] sel_t; - - typedef struct packed { - addr_t paddr; - axi_pkg::prot_t pprot; // same as AXI, this is allowed - logic psel; // onehot - logic penable; - logic pwrite; - data_t pwdata; - strb_t pstrb; - } apb_req_t; - - typedef struct packed { - logic pready; - data_t prdata; - logic pslverr; - } apb_resp_t; - - localparam rule_t [NoAddrRules-1:0] AddrMap = '{ - '{idx: 32'd7, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, - '{idx: 32'd6, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, - '{idx: 32'd5, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, - '{idx: 32'd4, start_addr: 32'h0002_0000, end_addr: 32'h0002_1000}, - '{idx: 32'd4, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, - '{idx: 32'd3, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, - '{idx: 32'd2, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, - '{idx: 32'd1, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, - '{idx: 32'd0, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} - }; - - typedef axi_test::axi_lite_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - .MIN_ADDR ( 32'h0000_0000 ), - .MAX_ADDR ( 32'h0002_2000 ), - // Maximum number of open transactions - .MAX_READ_TXNS ( 32'd10 ), - .MAX_WRITE_TXNS ( 32'd10 ), - // Upper and lower bounds on wait cycles on Ax, W, and resp (R and B) channels - .AX_MIN_WAIT_CYCLES ( 0 ), - .AX_MAX_WAIT_CYCLES ( 10 ), - .W_MIN_WAIT_CYCLES ( 0 ), - .W_MAX_WAIT_CYCLES ( 5 ), - .RESP_MIN_WAIT_CYCLES ( 0 ), - .RESP_MAX_WAIT_CYCLES ( 20 ) - ) axi_lite_rand_master_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - // DUT signals - logic rst_n; - logic end_of_sim; - - // master structs - axi_req_t axi_req; - axi_resp_t axi_resp; - - // slave structs - apb_req_t [NoApbSlaves-1:0] apb_req; - apb_resp_t [NoApbSlaves-1:0] apb_resps; - - // ------------------------------- - // AXI Interfaces - // ------------------------------- - AXI_LITE #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master (); - AXI_LITE_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master_dv (clk); - `AXI_LITE_ASSIGN(master, master_dv) - `AXI_LITE_ASSIGN_TO_REQ(axi_req, master) - `AXI_LITE_ASSIGN_FROM_RESP(master, axi_resp) - - // ------------------------------- - // AXI Rand Masters - // ------------------------------- - // Master controls simulation run time - initial begin : proc_axi_master - static axi_lite_rand_master_t axi_lite_rand_master = new ( master_dv , "axi_lite_mst"); - end_of_sim <= 1'b0; - axi_lite_rand_master.reset(); - @(posedge rst_n); - axi_lite_rand_master.run(NoReads, NoWrites); - end_of_sim <= 1'b1; - end - - - for (genvar i = 0; i < NoApbSlaves; i++) begin : gen_apb_slave - initial begin : proc_apb_slave - apb_resps[i] <= '0; - forever begin - @(posedge clk); - apb_resps[i].pready <= #ApplTime $urandom(); - apb_resps[i].prdata <= #ApplTime $urandom(); - apb_resps[i].pslverr <= #ApplTime $urandom(); - end - end - end - - initial begin : proc_sim_stop - @(posedge rst_n); - wait (end_of_sim); - $stop(); - end - - // pragma translate_off - `ifndef VERILATOR - // Assertions to determine correct APB protocol sequencing - default disable iff (!rst_n); - for (genvar i = 0; i < NoApbSlaves; i++) begin : gen_apb_assertions - // when psel is not asserted, the bus is in the idle state - sequence APB_IDLE; - !apb_req[i].psel; - endsequence - - // when psel is set and penable is not, it is the setup state - sequence APB_SETUP; - apb_req[i].psel && !apb_req[i].penable; - endsequence - - // when psel and penable are set it is the access state - sequence APB_ACCESS; - apb_req[i].psel && apb_req[i].penable; - endsequence - - // APB Transfer is APB state going from setup to access - sequence APB_TRANSFER; - APB_SETUP ##1 APB_ACCESS; - endsequence - - apb_complete: assert property ( @(posedge clk) - (APB_SETUP |-> APB_TRANSFER)); - - apb_penable: assert property ( @(posedge clk) - (apb_req[i].penable && apb_req[i].psel && apb_resps[i].pready |=> (!apb_req[i].penable))); - - control_stable: assert property ( @(posedge clk) - (APB_TRANSFER |-> $stable({apb_req[i].pwrite, apb_req[i].paddr}))); - - apb_valid: assert property ( @(posedge clk) - (APB_TRANSFER |-> ((!{apb_req[i].pwrite, apb_req[i].pstrb, apb_req[i].paddr}) !== 1'bx))); - - write_stable: assert property ( @(posedge clk) - ((apb_req[i].penable && apb_req[i].pwrite) |-> $stable(apb_req[i].pwdata))); - - strb_stable: assert property ( @(posedge clk) - ((apb_req[i].penable && apb_req[i].pwrite) |-> $stable(apb_req[i].pstrb))); - end - `endif - // pragma translate_on - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o ( clk ), - .rst_no ( rst_n ) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_lite_to_apb #( - .NoApbSlaves ( NoApbSlaves ), - .NoRules ( NoAddrRules ), - .AddrWidth ( AxiAddrWidth ), - .DataWidth ( AxiDataWidth ), - .PipelineRequest ( TbPipelineRequest ), - .PipelineResponse ( TbPipelineResponse ), - .axi_lite_req_t ( axi_req_t ), - .axi_lite_resp_t ( axi_resp_t ), - .apb_req_t ( apb_req_t ), - .apb_resp_t ( apb_resp_t ), - .rule_t ( rule_t ) - ) i_axi_lite_to_apb_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .axi_lite_req_i ( axi_req ), - .axi_lite_resp_o ( axi_resp ), - .apb_req_o ( apb_req ), - .apb_resp_i ( apb_resps ), - .addr_map_i ( AddrMap ) - ); -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_to_axi.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_lite_to_axi.sv deleted file mode 100644 index 497ce893e..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_to_axi.sv +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Fabian Schuiki -// - Andreas Kurth -// - Florian Zaruba - -`include "axi/assign.svh" - -module tb_axi_lite_to_axi; - - parameter TB_AW = 32; - parameter TB_DW = 32; - parameter TB_IW = 8; - parameter TB_UW = 8; - - localparam tCK = 1ns; - - logic clk = 0; - logic rst = 1; - logic done = 0; - - AXI_LITE_DV #( - .AXI_ADDR_WIDTH(TB_AW), - .AXI_DATA_WIDTH(TB_DW) - ) axi_lite_dv(clk); - - AXI_LITE #( - .AXI_ADDR_WIDTH(TB_AW), - .AXI_DATA_WIDTH(TB_DW) - ) axi_lite(); - - `AXI_LITE_ASSIGN(axi_lite, axi_lite_dv) - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(TB_AW), - .AXI_DATA_WIDTH(TB_DW), - .AXI_ID_WIDTH(TB_IW), - .AXI_USER_WIDTH(TB_UW) - ) axi_dv(clk); - - AXI_BUS #( - .AXI_ADDR_WIDTH(TB_AW), - .AXI_DATA_WIDTH(TB_DW), - .AXI_ID_WIDTH(TB_IW), - .AXI_USER_WIDTH(TB_UW) - ) axi(); - - `AXI_ASSIGN(axi_dv, axi) - - axi_lite_to_axi_intf #( - .AXI_DATA_WIDTH (TB_DW) - ) i_dut ( - .in ( axi_lite ), - .slv_aw_cache_i ('0), - .slv_ar_cache_i ('0), - .out ( axi ) - ); - - axi_test::axi_lite_driver #(.AW(TB_AW), .DW(TB_DW)) axi_lite_drv = new(axi_lite_dv); - axi_test::axi_driver #(.AW(TB_AW), .DW(TB_DW), .IW(TB_IW), .UW(TB_UW)) axi_drv = new(axi_dv); - - initial begin - #tCK; - rst <= 0; - #tCK; - rst <= 1; - #tCK; - while (!done) begin - clk <= 1; - #(tCK/2); - clk <= 0; - #(tCK/2); - end - end - - initial begin - automatic axi_pkg::resp_t resp; - axi_lite_drv.reset_master(); - @(posedge clk); - axi_lite_drv.send_aw('hdeadbeef, axi_pkg::prot_t'('0)); - axi_lite_drv.send_w('hdeadbeef, '1); - axi_lite_drv.recv_b(resp); - $info("AXI-Lite B: resp %h", resp); - repeat (4) @(posedge clk); - done = 1; - end - - initial begin - automatic axi_test::axi_ax_beat #(.AW(TB_AW), .IW(TB_IW), .UW(TB_UW)) ax_beat; - automatic axi_test::axi_w_beat #(.DW(TB_DW), .UW(TB_UW)) w_beat; - automatic axi_test::axi_b_beat #(.IW(TB_IW), .UW(TB_UW)) b_beat = new; - axi_drv.reset_slave(); - @(posedge clk); - axi_drv.recv_aw(ax_beat); - $info("AXI AW: addr %h", ax_beat.ax_addr); - axi_drv.recv_w(w_beat); - $info("AXI W: data %h, strb %h", w_beat.w_data, w_beat.w_strb); - axi_drv.send_b(b_beat); - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_xbar.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_lite_xbar.sv deleted file mode 100644 index f37393c68..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_lite_xbar.sv +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger -// - Fabian Schuiki -// - Andreas Kurth - -// Directed Random Verification Testbench for `axi_lite_xbar`: The crossbar is instantiated with -// a number of random axi master and slave modules. Each random master executes a fixed number of -// writes and reads over the whole addess map. All masters simultaneously issue transactions -// through the crossbar, thereby fully saturating all its bandwidth. - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_lite_xbar; - // Dut parameters - localparam int unsigned NoMasters = 32'd6; // How many Axi Masters there are - localparam int unsigned NoSlaves = 32'd8; // How many Axi Slaves there are - // Random master no Transactions - localparam int unsigned NoWrites = 32'd10000; // How many writes per master - localparam int unsigned NoReads = 32'd10000; // How many reads per master - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - // axi configuration - localparam int unsigned AxiAddrWidth = 32'd32; // Axi Address Width - localparam int unsigned AxiDataWidth = 32'd64; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 32'd8; - // in the bench can change this variables which are set here freely - localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ - NoSlvPorts: NoMasters, - NoMstPorts: NoSlaves, - MaxMstTrans: 32'd10, - MaxSlvTrans: 32'd6, - FallThrough: 1'b0, - LatencyMode: axi_pkg::CUT_ALL_AX, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: 32'd8, - default: '0 - }; - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - - `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, addr_t) - `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_t, data_t, strb_t) - `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) - - `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, addr_t) - `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_t, data_t) - - `AXI_LITE_TYPEDEF_REQ_T(req_lite_t, aw_chan_lite_t, w_chan_lite_t, ar_chan_lite_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_lite_t, b_chan_lite_t, r_chan_lite_t) - - localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = '{ - '{idx: 32'd7, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, - '{idx: 32'd6, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, - '{idx: 32'd5, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, - '{idx: 32'd4, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, - '{idx: 32'd3, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, - '{idx: 32'd2, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, - '{idx: 32'd1, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, - '{idx: 32'd0, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} - }; - - typedef axi_test::axi_lite_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - .MIN_ADDR ( 32'h0000_0000 ), - .MAX_ADDR ( 32'h0001_3000 ), - .MAX_READ_TXNS ( 10 ), - .MAX_WRITE_TXNS ( 10 ) - ) rand_lite_master_t; - typedef axi_test::axi_lite_rand_slave #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) - ) rand_lite_slave_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - // DUT signals - logic rst_n; - logic [NoMasters-1:0] end_of_sim; - - // master structs - req_lite_t [NoMasters-1:0] masters_req; - resp_lite_t [NoMasters-1:0] masters_resp; - - // slave structs - req_lite_t [NoSlaves-1:0] slaves_req; - resp_lite_t [NoSlaves-1:0] slaves_resp; - - // ------------------------------- - // AXI Interfaces - // ------------------------------- - AXI_LITE #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master [NoMasters-1:0] (); - AXI_LITE_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) master_dv [NoMasters-1:0] (clk); - for (genvar i = 0; i < NoMasters; i++) begin : gen_conn_dv_masters - `AXI_LITE_ASSIGN(master[i], master_dv[i]) - `AXI_LITE_ASSIGN_TO_REQ(masters_req[i], master[i]) - `AXI_LITE_ASSIGN_FROM_RESP(master[i], masters_resp[i]) - end - - AXI_LITE #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) slave [NoSlaves-1:0] (); - AXI_LITE_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ) - ) slave_dv [NoSlaves-1:0](clk); - for (genvar i = 0; i < NoSlaves; i++) begin : gen_conn_dv_slaves - `AXI_LITE_ASSIGN(slave_dv[i], slave[i]) - `AXI_LITE_ASSIGN_FROM_REQ(slave[i], slaves_req[i]) - `AXI_LITE_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) - end - // ------------------------------- - // AXI Rand Masters and Slaves - // ------------------------------- - // Masters control simulation run time - for (genvar i = 0; i < NoMasters; i++) begin : gen_rand_master - initial begin : proc_generate_traffic - automatic rand_lite_master_t lite_axi_master = new ( master_dv[i], $sformatf("MST_%0d", i)); - automatic data_t data = '0; - automatic axi_pkg::resp_t resp = '0; - end_of_sim[i] <= 1'b0; - lite_axi_master.reset(); - @(posedge rst_n); - lite_axi_master.write(32'h0000_1100, axi_pkg::prot_t'('0), 64'hDEADBEEFDEADBEEF, 8'hFF, resp); - lite_axi_master.read(32'h0000_e100, axi_pkg::prot_t'('0), data, resp); - lite_axi_master.run(NoReads, NoWrites); - end_of_sim[i] <= 1'b1; - end - end - - for (genvar i = 0; i < NoSlaves; i++) begin : gen_rand_slave - initial begin : proc_recieve_traffic - automatic rand_lite_slave_t lite_axi_slave = new( slave_dv[i] , $sformatf("SLV_%0d", i)); - lite_axi_slave.reset(); - @(posedge rst_n); - lite_axi_slave.run(); - end - end - - initial begin : proc_stop_sim - wait (&end_of_sim); - repeat (1000) @(posedge clk); - $display("Simulation stopped as all Masters transferred their data, Success.",); - $stop(); - end - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_lite_xbar #( - .Cfg ( xbar_cfg ), - .aw_chan_t ( aw_chan_lite_t ), - .w_chan_t ( w_chan_lite_t ), - .b_chan_t ( b_chan_lite_t ), - .ar_chan_t ( ar_chan_lite_t ), - .r_chan_t ( r_chan_lite_t ), - .req_t ( req_lite_t ), - .resp_t ( resp_lite_t ), - .rule_t ( rule_t ) - ) i_xbar_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_i ( 1'b0 ), - .slv_ports_req_i ( masters_req ), - .slv_ports_resp_o ( masters_resp ), - .mst_ports_req_o ( slaves_req ), - .mst_ports_resp_i ( slaves_resp ), - .addr_map_i ( AddrMap ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) - ); -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_modify_address.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_modify_address.sv deleted file mode 100644 index 3fab7ff42..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_modify_address.sv +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2020 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Andreas Kurth - -`include "axi/assign.svh" -`include "axi/typedef.svh" - -/// Testbench for `axi_modify_address` -module tb_axi_modify_address #( - // DUT Parameters - parameter int unsigned AXI_SLV_PORT_ADDR_WIDTH = 32, - parameter int unsigned AXI_MST_PORT_ADDR_WIDTH = 48, - parameter int unsigned AXI_DATA_WIDTH = 64, - parameter int unsigned AXI_ID_WIDTH = 3, - parameter int unsigned AXI_USER_WIDTH = 2, - // TB Parameters - parameter time TCLK = 10ns, - parameter time TA = TCLK * 1/4, - parameter time TT = TCLK * 3/4, - parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, - parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, - parameter int unsigned RESP_MIN_WAIT_CYCLES = 0, - parameter int unsigned RESP_MAX_WAIT_CYCLES = REQ_MAX_WAIT_CYCLES/2, - parameter int unsigned N_TXNS = 1000 -); - - localparam int unsigned N_RD_TXNS = N_TXNS / 2; - localparam int unsigned N_WR_TXNS = N_TXNS / 2; - - // Clock and Reset - logic clk, - rst_n; - clk_rst_gen #( - .ClkPeriod (TCLK), - .RstClkCycles (5) - ) i_clk_rst_gen ( - .clk_o (clk), - .rst_no (rst_n) - ); - - // AXI Interfaces - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (AXI_SLV_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) upstream_dv ( - .clk_i (clk) - ); - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_SLV_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) upstream (); - `AXI_ASSIGN(upstream, upstream_dv) - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (AXI_MST_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) downstream_dv ( - .clk_i (clk) - ); - AXI_BUS #( - .AXI_ADDR_WIDTH (AXI_MST_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) downstream (); - `AXI_ASSIGN(downstream_dv, downstream) - - // Types - typedef logic [AXI_MST_PORT_ADDR_WIDTH-1:0] addr_t; - typedef logic [AXI_DATA_WIDTH-1:0] data_t; - typedef logic [AXI_ID_WIDTH-1:0] id_t; - typedef logic [AXI_MST_PORT_ADDR_WIDTH-13:0] page_t; - typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; - typedef logic [AXI_USER_WIDTH-1:0] user_t; - `AXI_TYPEDEF_AW_CHAN_T(aw_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_t, id_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_t, data_t, id_t, user_t) - - // DUT - addr_t mst_aw_addr, - mst_ar_addr; - axi_modify_address_intf #( - .AXI_SLV_PORT_ADDR_WIDTH (AXI_SLV_PORT_ADDR_WIDTH), - .AXI_MST_PORT_ADDR_WIDTH (AXI_MST_PORT_ADDR_WIDTH), - .AXI_DATA_WIDTH (AXI_DATA_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .AXI_USER_WIDTH (AXI_USER_WIDTH) - ) i_dut ( - .slv (upstream), - .mst_aw_addr_i (mst_aw_addr), - .mst_ar_addr_i (mst_ar_addr), - .mst (downstream) - ); - - // Test harness master - typedef axi_test::axi_rand_master #( - .AW (AXI_SLV_PORT_ADDR_WIDTH), - .DW (AXI_DATA_WIDTH), - .IW (AXI_ID_WIDTH), - .UW (AXI_USER_WIDTH), - .TA (TA), - .TT (TT), - .MAX_READ_TXNS (N_TXNS), - .MAX_WRITE_TXNS (N_TXNS), - .AX_MIN_WAIT_CYCLES (REQ_MIN_WAIT_CYCLES), - .AX_MAX_WAIT_CYCLES (REQ_MAX_WAIT_CYCLES), - .W_MIN_WAIT_CYCLES (REQ_MIN_WAIT_CYCLES), - .W_MAX_WAIT_CYCLES (REQ_MAX_WAIT_CYCLES), - .RESP_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .RESP_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), - .AXI_MAX_BURST_LEN (16) - ) axi_master_t; - axi_master_t axi_master = new(upstream_dv); - initial begin - wait (rst_n); - axi_master.run(N_RD_TXNS, N_WR_TXNS); - #(10*TCLK); - $finish(); - end - - // Test harness slave - typedef axi_test::axi_rand_slave #( - .AW (AXI_MST_PORT_ADDR_WIDTH), - .DW (AXI_DATA_WIDTH), - .IW (AXI_ID_WIDTH), - .UW (AXI_USER_WIDTH), - .TA (TA), - .TT (TT), - .AX_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .AX_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), - .R_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .R_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), - .RESP_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), - .RESP_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES) - ) axi_slave_t; - axi_slave_t axi_slave = new(downstream_dv); - initial begin - wait (rst_n); - axi_slave.run(); - end - - // Assign offset within page from upstream. - assign mst_aw_addr[11:0] = upstream.aw_addr[11:0]; - assign mst_ar_addr[11:0] = upstream.ar_addr[11:0]; - - // Randomize page number. - page_t mst_aw_page, - mst_ar_page; - assign mst_aw_addr[AXI_MST_PORT_ADDR_WIDTH-1:12] = mst_aw_page; - assign mst_ar_addr[AXI_MST_PORT_ADDR_WIDTH-1:12] = mst_ar_page; - initial begin - logic rand_success; - mst_aw_page = '0; - mst_ar_page = '0; - wait (rst_n); - forever begin - @(posedge clk); - #TA; - if (!(upstream.aw_valid && !upstream.aw_ready)) begin - rand_success = std::randomize(mst_aw_page); - assert(rand_success); - end - if (!(upstream.ar_valid && !upstream.ar_ready)) begin - rand_success = std::randomize(mst_ar_page); - assert(rand_success); - end - end - end - - // Signals for expected and actual responses - aw_t aw_exp, aw_act; - w_t w_exp, w_act; - b_t b_exp, b_act; - ar_t ar_exp, ar_act; - r_t r_exp, r_act; - - // Compute expected responses. - always_comb begin - `AXI_SET_TO_AW(aw_exp, upstream) - aw_exp.addr = mst_aw_addr; - `AXI_SET_TO_AR(ar_exp, upstream) - ar_exp.addr = mst_ar_addr; - end - `AXI_ASSIGN_TO_W(w_exp, upstream) - `AXI_ASSIGN_TO_B(b_exp, downstream) - `AXI_ASSIGN_TO_R(r_exp, downstream) - - // Determine actual responses. - `AXI_ASSIGN_TO_AW(aw_act, downstream) - `AXI_ASSIGN_TO_W(w_act, downstream) - `AXI_ASSIGN_TO_B(b_act, upstream) - `AXI_ASSIGN_TO_AR(ar_act, downstream) - `AXI_ASSIGN_TO_R(r_act, upstream) - - // Assert that actual responses match expected responses. - default disable iff (~rst_n); - aw: assert property(@(posedge clk) - downstream.aw_valid |-> aw_act == aw_exp - ) else $error("AW %p != %p!", aw_act, aw_exp); - w: assert property(@(posedge clk) - downstream.w_valid |-> w_act == w_exp - ) else $error("W %p != %p!", w_act, w_exp); - b: assert property(@(posedge clk) - upstream.b_valid |-> b_act == b_exp - ) else $error("B %p != %p!", b_act, b_exp); - ar: assert property(@(posedge clk) - downstream.ar_valid |-> ar_act == ar_exp - ) else $error("AR %p != %p!", ar_act, ar_exp); - r: assert property(@(posedge clk) - upstream.r_valid |-> r_act == r_exp - ) else $error("R %p != %p!", r_act, r_exp); - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_serializer.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_serializer.sv deleted file mode 100644 index 10978cfbf..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_serializer.sv +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Wolfgang Roenninger -// - Andreas Kurth - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_serializer #( - parameter int unsigned NoWrites = 5000, // How many writes per master - parameter int unsigned NoReads = 3000 // How many reads per master - ); - // Random master no Transactions - localparam int unsigned NoPendingDut = 4; - // Random Master Atomics - localparam int unsigned MaxAW = 32'd30; - localparam int unsigned MaxAR = 32'd30; - localparam bit EnAtop = 1'b1; - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - // AXI configuration - localparam int unsigned AxiIdWidth = 4; - localparam int unsigned AxiAddrWidth = 32; // Axi Address Width - localparam int unsigned AxiDataWidth = 64; // Axi Data Width - localparam int unsigned AxiUserWidth = 5; - // Sim print config, how many transactions - localparam int unsigned PrintTxn = 500; - - typedef axi_test::axi_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidth ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( MaxAR ), - .MAX_WRITE_TXNS ( MaxAW ), - .AXI_ATOPS ( EnAtop ) - ) axi_rand_master_t; - typedef axi_test::axi_rand_slave #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidth ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) - ) axi_rand_slave_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - logic rst_n; - logic end_of_sim; - - // interfaces - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv (clk); - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidth ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv (clk); - - `AXI_ASSIGN(master, master_dv) - `AXI_ASSIGN(slave_dv, slave) - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_serializer_intf #( - .MAX_READ_TXNS ( NoPendingDut ), - .MAX_WRITE_TXNS ( NoPendingDut ), - .AXI_ID_WIDTH ( AxiIdWidth ), // AXI ID width - .AXI_ADDR_WIDTH ( AxiAddrWidth ), // AXI address width - .AXI_DATA_WIDTH ( AxiDataWidth ), // AXI data width - .AXI_USER_WIDTH ( AxiUserWidth ) // AXI user width - ) i_dut ( - .clk_i ( clk ), // clock - .rst_ni ( rst_n ), // asynchronous reset active low - .slv ( master ), // slave port - .mst ( slave ) // master port - ); - - initial begin : proc_axi_master - automatic axi_rand_master_t axi_rand_master = new(master_dv); - end_of_sim <= 1'b0; - axi_rand_master.add_memory_region(32'h0000_0000, 32'h1000_0000, axi_pkg::DEVICE_NONBUFFERABLE); - axi_rand_master.add_memory_region(32'h2000_0000, 32'h3000_0000, axi_pkg::WTHRU_NOALLOCATE); - axi_rand_master.add_memory_region(32'h4000_0000, 32'h5000_0000, axi_pkg::WBACK_RWALLOCATE); - axi_rand_master.reset(); - @(posedge rst_n); - axi_rand_master.run(NoReads, NoWrites); - end_of_sim <= 1'b1; - repeat (100) @(posedge clk); - $stop(); - end - - initial begin : proc_axi_slave - automatic axi_rand_slave_t axi_rand_slave = new(slave_dv); - axi_rand_slave.reset(); - @(posedge rst_n); - axi_rand_slave.run(); - end - - // Checker - typedef logic [AxiIdWidth-1:0] axi_id_t; - typedef logic [AxiIdWidth-1:0] axi_addr_t; - typedef logic [AxiIdWidth-1:0] axi_data_t; - typedef logic [AxiIdWidth-1:0] axi_strb_t; - typedef logic [AxiIdWidth-1:0] axi_user_t; - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, axi_addr_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, axi_data_t, axi_strb_t, axi_user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, axi_addr_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_t, axi_data_t, axi_id_t, axi_user_t) - axi_id_t aw_queue[$]; - axi_id_t ar_queue[$]; - aw_chan_t aw_chan[$]; - w_chan_t w_chan[$]; - b_chan_t b_chan[$]; - ar_chan_t ar_chan[$]; - r_chan_t r_chan[$]; - - initial begin : proc_checker - automatic axi_id_t id_exp; // expected ID - automatic aw_chan_t aw_exp; // expected AW - automatic aw_chan_t aw_act; // actual AW - automatic w_chan_t w_exp; // expected W - automatic w_chan_t w_act; // actual W - automatic b_chan_t b_exp; // expected B - automatic b_chan_t b_act; // actual B - automatic ar_chan_t ar_exp; // expected AR - automatic ar_chan_t ar_act; // actual AR - automatic r_chan_t r_exp; // expected R - automatic r_chan_t r_act; // actual R - forever begin - @(posedge clk); - #TestTime; - // All FIFOs get populated if there is something to put in - if (master.aw_valid && master.aw_ready) begin - `AXI_SET_TO_AW(aw_exp, master) - aw_exp.id = '0; - id_exp = master.aw_id; - aw_chan.push_back(aw_exp); - aw_queue.push_back(id_exp); - if (master.aw_atop[axi_pkg::ATOP_R_RESP]) begin - ar_queue.push_back(id_exp); - end - end - if (master.w_valid && master.w_ready) begin - `AXI_SET_TO_W(w_exp, master) - w_chan.push_back(w_exp); - end - if (slave.b_valid && slave.b_ready) begin - id_exp = aw_queue.pop_front(); - `AXI_SET_TO_B(b_exp, slave) - b_exp.id = id_exp; - b_chan.push_back(b_exp); - end - if (master.ar_valid && master.ar_ready) begin - `AXI_SET_TO_AR(ar_exp, master) - ar_exp.id = '0; - id_exp = master.ar_id; - ar_chan.push_back(ar_exp); - ar_queue.push_back(id_exp); - end - if (slave.r_valid && slave.r_ready) begin - `AXI_SET_TO_R(r_exp, slave) - if (slave.r_last) begin - id_exp = ar_queue.pop_front(); - end else begin - id_exp = ar_queue[0]; - end - r_exp.id = id_exp; - r_chan.push_back(r_exp); - end - // Check that all channels match the expected response - if (slave.aw_valid && slave.aw_ready) begin - aw_exp = aw_chan.pop_front(); - `AXI_SET_TO_AW(aw_act, slave) - assert(aw_act == aw_exp) else $error("AW Measured: %h Expected: %h", aw_act, aw_exp); - end - if (slave.w_valid && slave.w_ready) begin - w_exp = w_chan.pop_front(); - `AXI_SET_TO_W(w_act, slave) - assert(w_act == w_exp) else $error("W Measured: %h Expected: %h", w_act, w_exp); - end - if (master.b_valid && master.b_ready) begin - b_exp = b_chan.pop_front(); - `AXI_SET_TO_B(b_act, master) - assert(b_act == b_exp) else $error("B Measured: %h Expected: %h", b_act, b_exp); - end - if (slave.ar_valid && slave.ar_ready) begin - ar_exp = ar_chan.pop_front(); - `AXI_SET_TO_AR(ar_act, slave) - assert(ar_act == ar_exp) else $error("AR Measured: %h Expected: %h", ar_act, ar_exp); - end - if (master.r_valid && master.r_ready) begin - r_exp = r_chan.pop_front(); - `AXI_SET_TO_R(r_act, master) - assert(r_act == r_exp) else $error("R Measured: %h Expected: %h", r_act, r_exp); - end - end - end - - initial begin : proc_sim_progress - automatic int unsigned aw = 0; - automatic int unsigned ar = 0; - automatic bit aw_printed = 1'b0; - automatic bit ar_printed = 1'b0; - - @(posedge rst_n); - - forever begin - @(posedge clk); - #TestTime; - if (master.aw_valid && master.aw_ready) begin - aw++; - end - if (master.ar_valid && master.ar_ready) begin - ar++; - end - - if ((aw % PrintTxn == 0) && ! aw_printed) begin - $display("%t> Transmit AW %d of %d.", $time(), aw, NoWrites); - aw_printed = 1'b1; - end - if ((ar % PrintTxn == 0) && !ar_printed) begin - $display("%t> Transmit AR %d of %d.", $time(), ar, NoReads); - ar_printed = 1'b1; - end - - if (aw % PrintTxn == 1) begin - aw_printed = 1'b0; - end - if (ar % PrintTxn == 1) begin - ar_printed = 1'b0; - end - - if (end_of_sim) begin - $info("All transactions completed."); - break; - end - end - end -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_sim_mem.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_sim_mem.sv deleted file mode 100644 index 711dba0f6..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_sim_mem.sv +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2020 ETH Zurich and University of Bologna -// SPDX-License-Identifier: SHL-0.51 -// -// Authors: -// - Andreas Kurth - -`include "axi/assign.svh" -`include "axi/typedef.svh" - -/// Testbench for `axi_sim_mem` -module tb_axi_sim_mem #( - // TB Parameters - parameter time TbTclk = 10ns, - // Module Parameters - parameter int unsigned TbAddrWidth = 32'd64, - parameter int unsigned TbDataWidth = 32'd128, - parameter int unsigned TbIdWidth = 32'd6, - parameter int unsigned TbUserWidth = 32'd2, - parameter bit TbWarnUninitialized = 1'b0, - parameter time TbApplDelay = 2ns, - parameter time TbAcqDelay = 8ns -); - - logic clk, - rst_n; - clk_rst_gen #( - .ClkPeriod (TbTclk), - .RstClkCycles (5) - ) i_clk_rst_gen ( - .clk_o (clk), - .rst_no (rst_n) - ); - - localparam int unsigned StrbWidth = TbDataWidth / 8; - typedef logic [TbAddrWidth-1:0] addr_t; - typedef logic [TbDataWidth-1:0] data_t; - typedef logic [TbIdWidth-1:0] id_t; - typedef logic [StrbWidth-1:0] strb_t; - typedef logic [TbUserWidth-1:0] user_t; - `AXI_TYPEDEF_AW_CHAN_T(aw_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_t, id_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_t, data_t, id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_t, w_t, ar_t) - `AXI_TYPEDEF_RESP_T(rsp_t, b_t, r_t) - - req_t req; - rsp_t rsp; - axi_sim_mem #( - .AddrWidth (TbAddrWidth), - .DataWidth (TbDataWidth), - .IdWidth (TbIdWidth), - .UserWidth (TbUserWidth), - .req_t (req_t), - .rsp_t (rsp_t), - .WarnUninitialized (TbWarnUninitialized), - .ApplDelay (TbApplDelay), - .AcqDelay (TbAcqDelay) - ) i_sim_mem ( - .clk_i (clk), - .rst_ni (rst_n), - .axi_req_i (req), - .axi_rsp_o (rsp) - ); - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH (TbAddrWidth), - .AXI_DATA_WIDTH (TbDataWidth), - .AXI_ID_WIDTH (TbIdWidth), - .AXI_USER_WIDTH (TbUserWidth) - ) axi_dv (clk); - `AXI_ASSIGN_TO_REQ(req, axi_dv) - `AXI_ASSIGN_FROM_RESP(axi_dv, rsp) - typedef axi_test::axi_driver #( - .AW(TbAddrWidth), .DW(TbDataWidth), .IW(TbIdWidth), .UW(TbUserWidth), - .TA(1ns), .TT(6ns) - ) drv_t; - drv_t drv = new(axi_dv); - - // Simply read and write a random memory region. - initial begin - automatic logic rand_success; - automatic data_t exp_data[$]; - automatic drv_t::ax_beat_t aw_beat = new, ar_beat = new; - automatic drv_t::w_beat_t w_beat = new; - automatic drv_t::b_beat_t b_beat; - automatic drv_t::r_beat_t r_beat; - drv.reset_master(); - wait (rst_n); - // AW - rand_success = aw_beat.randomize(); assert(rand_success); - aw_beat.ax_addr >>= $clog2(StrbWidth); // align address with data width - aw_beat.ax_addr <<= $clog2(StrbWidth); - aw_beat.ax_len = $urandom(); - aw_beat.ax_size = $clog2(StrbWidth); - aw_beat.ax_burst = axi_pkg::BURST_INCR; - drv.send_aw(aw_beat); - // W beats - for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin - rand_success = w_beat.randomize(); assert(rand_success); - w_beat.w_strb = '1; - if (i == aw_beat.ax_len) begin - w_beat.w_last = 1'b1; - end - drv.send_w(w_beat); - exp_data.push_back(w_beat.w_data); - end - // B - drv.recv_b(b_beat); - assert(b_beat.b_resp == axi_pkg::RESP_OKAY); - // AR - ar_beat.ax_addr = aw_beat.ax_addr; - ar_beat.ax_len = aw_beat.ax_len; - ar_beat.ax_size = aw_beat.ax_size; - ar_beat.ax_burst = aw_beat.ax_burst; - drv.send_ar(ar_beat); - // R beats - for (int unsigned i = 0; i <= ar_beat.ax_len; i++) begin - automatic data_t exp = exp_data.pop_front(); - drv.recv_r(r_beat); - assert(r_beat.r_data == exp) else - $error("Received 0x%h != expected 0x%h!", r_beat.r_data, exp); - end - // Done. - #(TbTclk); - $finish(); - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_to_axi_lite.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_to_axi_lite.sv deleted file mode 100644 index e854cb106..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_to_axi_lite.sv +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Fabian Schuiki -// - Wolfgang Roenninger -// - Andreas Kurth -// - Florian Zaruba - -`include "axi/assign.svh" - -module tb_axi_to_axi_lite; - - parameter AW = 32; - parameter DW = 32; - parameter IW = 8; - parameter UW = 8; - - localparam tCK = 1ns; - localparam TA = tCK * 1/4; - localparam TT = tCK * 3/4; - - localparam MAX_READ_TXNS = 32'd20; - localparam MAX_WRITE_TXNS = 32'd20; - localparam bit AXI_ATOPS = 1'b1; - - logic clk = 0; - logic rst = 1; - logic done = 0; - - AXI_LITE_DV #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW) - ) axi_lite_dv(clk); - - AXI_LITE #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW) - ) axi_lite(); - - `AXI_LITE_ASSIGN(axi_lite_dv, axi_lite) - - AXI_BUS_DV #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW), - .AXI_ID_WIDTH(IW), - .AXI_USER_WIDTH(UW) - ) axi_dv(clk); - - AXI_BUS #( - .AXI_ADDR_WIDTH(AW), - .AXI_DATA_WIDTH(DW), - .AXI_ID_WIDTH(IW), - .AXI_USER_WIDTH(UW) - ) axi(); - - `AXI_ASSIGN(axi, axi_dv) - - axi_to_axi_lite_intf #( - .AXI_ID_WIDTH ( IW ), - .AXI_ADDR_WIDTH ( AW ), - .AXI_DATA_WIDTH ( DW ), - .AXI_USER_WIDTH ( UW ), - .AXI_MAX_WRITE_TXNS ( 32'd10 ), - .AXI_MAX_READ_TXNS ( 32'd10 ), - .FALL_THROUGH ( 1'b1 ) - ) i_dut ( - .clk_i ( clk ), - .rst_ni ( rst ), - .testmode_i ( 1'b0 ), - .slv ( axi ), - .mst ( axi_lite ) - ); - - typedef axi_test::axi_rand_master #( - // AXI interface parameters - .AW ( AW ), - .DW ( DW ), - .IW ( IW ), - .UW ( UW ), - // Stimuli application and test time - .TA ( TA ), - .TT ( TT ), - // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( MAX_READ_TXNS ), - .MAX_WRITE_TXNS ( MAX_WRITE_TXNS ), - .AXI_ATOPS ( AXI_ATOPS ) - ) axi_rand_master_t; - typedef axi_test::axi_lite_rand_slave #(.AW(AW), .DW(DW), .TA(TA), .TT(TT)) axi_lite_rand_slv_t; - - axi_lite_rand_slv_t axi_lite_drv = new(axi_lite_dv, "axi_lite_rand_slave"); - axi_rand_master_t axi_drv = new(axi_dv); - - initial begin - #tCK; - rst <= 0; - #tCK; - rst <= 1; - #tCK; - while (!done) begin - clk <= 1; - #(tCK/2); - clk <= 0; - #(tCK/2); - end - end - - initial begin - axi_drv.add_memory_region(32'h0000_0000, - 32'h1000_0000, - axi_pkg::NORMAL_NONCACHEABLE_NONBUFFERABLE); - axi_drv.add_memory_region(32'h1000_0000, - 32'h2000_0000, - axi_pkg::NORMAL_NONCACHEABLE_BUFFERABLE); - axi_drv.add_memory_region(32'h3000_0000, - 32'h4000_0000, - axi_pkg::WBACK_RWALLOCATE); - axi_drv.reset(); - @(posedge rst); - axi_drv.run(1000, 2000); - - repeat (4) @(posedge clk); - done = 1'b1; - $info("All AXI4+ATOP Bursts converted to AXI4-Lite"); - repeat (4) @(posedge clk); - $stop(); - end - - initial begin - axi_lite_drv.reset(); - @(posedge clk); - - axi_lite_drv.run(); - end - - initial begin : proc_count_lite_beats - automatic longint aw_cnt = 0; - automatic longint w_cnt = 0; - automatic longint b_cnt = 0; - automatic longint ar_cnt = 0; - automatic longint r_cnt = 0; - @(posedge rst); - while (!done) begin - @(posedge clk); - #TT; - if (axi_lite.aw_valid && axi_lite.aw_ready) begin - aw_cnt++; - end - if (axi_lite.w_valid && axi_lite.w_ready) begin - w_cnt++; - end - if (axi_lite.b_valid && axi_lite.b_ready) begin - b_cnt++; - end - if (axi_lite.ar_valid && axi_lite.ar_ready) begin - ar_cnt++; - end - if (axi_lite.r_valid && axi_lite.r_ready) begin - r_cnt++; - end - end - assert (aw_cnt == w_cnt && w_cnt == b_cnt); - assert (ar_cnt == r_cnt); - $display("AXI4-Lite AW count: %0d", aw_cnt ); - $display("AXI4-Lite W count: %0d", w_cnt ); - $display("AXI4-Lite B count: %0d", b_cnt ); - $display("AXI4-Lite AR count: %0d", ar_cnt ); - $display("AXI4-Lite R count: %0d", r_cnt ); - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_xbar.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_xbar.sv deleted file mode 100644 index 195f0dde5..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_xbar.sv +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Florian Zaruba -// - Andreas Kurth - -// Directed Random Verification Testbench for `axi_xbar`: The crossbar is instantiated with -// a number of random axi master and slave modules. Each random master executes a fixed number of -// writes and reads over the whole addess map. All masters simultaneously issue transactions -// through the crossbar, thereby saturating it. A monitor, which snoops the transactions of each -// master and slave port and models the crossbar with a network of FIFOs, checks whether each -// transaction follows the expected route. - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_xbar #( - parameter bit TbEnAtop = 1'b1, // enable atomic operations (ATOPs) - parameter bit TbEnExcl = 1'b0, // enable exclusive accesses - parameter bit TbUniqueIds = 1'b0 // restrict to only unique IDs -); - // Dut parameters - localparam int unsigned NoMasters = 6; // How many Axi Masters there are - localparam int unsigned NoSlaves = 8; // How many Axi Slaves there are - // Random master no Transactions - localparam int unsigned NoWrites = 125; // How many writes per master - localparam int unsigned NoReads = 125; // How many reads per master - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - - // axi configuration - localparam int unsigned AxiIdWidthMasters = 4; - localparam int unsigned AxiIdUsed = 3; // Has to be <= AxiIdWidthMasters - localparam int unsigned AxiIdWidthSlaves = AxiIdWidthMasters + $clog2(NoMasters); - localparam int unsigned AxiAddrWidth = 32; // Axi Address Width - localparam int unsigned AxiDataWidth = 64; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; - localparam int unsigned AxiUserWidth = 5; - // in the bench can change this variables which are set here freely - localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ - NoSlvPorts: NoMasters, - NoMstPorts: NoSlaves, - MaxMstTrans: 10, - MaxSlvTrans: 6, - FallThrough: 1'b0, - LatencyMode: axi_pkg::CUT_ALL_AX, - AxiIdWidthSlvPorts: AxiIdWidthMasters, - AxiIdUsedSlvPorts: AxiIdUsed, - UniqueIds: TbUniqueIds, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: 8 - }; - typedef logic [AxiIdWidthMasters-1:0] id_mst_t; - typedef logic [AxiIdWidthSlaves-1:0] id_slv_t; - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - typedef logic [AxiUserWidth-1:0] user_t; - - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_mst_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_slv_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_mst_t, id_mst_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_slv_t, id_slv_t, user_t) - - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_mst_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_slv_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_mst_t, data_t, id_mst_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_slv_t, data_t, id_slv_t, user_t) - - `AXI_TYPEDEF_REQ_T(mst_req_t, aw_chan_mst_t, w_chan_t, ar_chan_mst_t) - `AXI_TYPEDEF_RESP_T(mst_resp_t, b_chan_mst_t, r_chan_mst_t) - `AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_slv_t, w_chan_t, ar_chan_slv_t) - `AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_slv_t, r_chan_slv_t) - - localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = '{ - '{idx: 32'd7, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, - '{idx: 32'd6, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, - '{idx: 32'd5, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, - '{idx: 32'd4, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, - '{idx: 32'd3, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, - '{idx: 32'd2, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, - '{idx: 32'd1, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, - '{idx: 32'd0, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} - }; - - typedef axi_test::axi_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthMasters ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( 20 ), - .MAX_WRITE_TXNS ( 20 ), - .AXI_EXCLS ( TbEnExcl ), - .AXI_ATOPS ( TbEnAtop ), - .UNIQUE_IDS ( TbUniqueIds ) - ) axi_rand_master_t; - typedef axi_test::axi_rand_slave #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthSlaves ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) - ) axi_rand_slave_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - // DUT signals - logic rst_n; - logic [NoMasters-1:0] end_of_sim; - - // master structs - mst_req_t [NoMasters-1:0] masters_req; - mst_resp_t [NoMasters-1:0] masters_resp; - - // slave structs - slv_req_t [NoSlaves-1:0] slaves_req; - slv_resp_t [NoSlaves-1:0] slaves_resp; - - // ------------------------------- - // AXI Interfaces - // ------------------------------- - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master [NoMasters-1:0] (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv [NoMasters-1:0] (clk); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_monitor_dv [NoMasters-1:0] (clk); - for (genvar i = 0; i < NoMasters; i++) begin : gen_conn_dv_masters - `AXI_ASSIGN (master[i], master_dv[i]) - `AXI_ASSIGN_TO_REQ(masters_req[i], master[i]) - `AXI_ASSIGN_FROM_RESP(master[i], masters_resp[i]) - end - - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave [NoSlaves-1:0] (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv [NoSlaves-1:0](clk); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_monitor_dv [NoSlaves-1:0](clk); - for (genvar i = 0; i < NoSlaves; i++) begin : gen_conn_dv_slaves - `AXI_ASSIGN(slave_dv[i], slave[i]) - `AXI_ASSIGN_FROM_REQ(slave[i], slaves_req[i]) - `AXI_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) - end - // ------------------------------- - // AXI Rand Masters and Slaves - // ------------------------------- - // Masters control simulation run time - for (genvar i = 0; i < NoMasters; i++) begin : gen_rand_master - static axi_rand_master_t axi_rand_master = new ( master_dv[i] ); - initial begin - end_of_sim[i] <= 1'b0; - axi_rand_master.add_memory_region(AddrMap[0].start_addr, - AddrMap[xbar_cfg.NoAddrRules-1].end_addr, - axi_pkg::DEVICE_NONBUFFERABLE); - axi_rand_master.reset(); - @(posedge rst_n); - axi_rand_master.run(NoReads, NoWrites); - end_of_sim[i] <= 1'b1; - end - end - - for (genvar i = 0; i < NoSlaves; i++) begin : gen_rand_slave - static axi_rand_slave_t axi_rand_slave = new( slave_dv[i] ); - initial begin - axi_rand_slave.reset(); - @(posedge rst_n); - axi_rand_slave.run(); - end - end - - initial begin : proc_monitor - static tb_axi_xbar_pkg::axi_xbar_monitor #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthMasters ( AxiIdWidthMasters ), - .AxiIdWidthSlaves ( AxiIdWidthSlaves ), - .AxiUserWidth ( AxiUserWidth ), - .NoMasters ( NoMasters ), - .NoSlaves ( NoSlaves ), - .NoAddrRules ( xbar_cfg.NoAddrRules ), - .rule_t ( rule_t ), - .AddrMap ( AddrMap ), - .TimeTest ( TestTime ) - ) monitor = new( master_monitor_dv, slave_monitor_dv ); - fork - monitor.run(); - do begin - #TestTime; - if(end_of_sim == '1) begin - monitor.print_result(); - $stop(); - end - @(posedge clk); - end while (1'b1); - join - end - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_xbar #( - .Cfg ( xbar_cfg ), - .slv_aw_chan_t( aw_chan_mst_t ), - .mst_aw_chan_t( aw_chan_slv_t ), - .w_chan_t ( w_chan_t ), - .slv_b_chan_t ( b_chan_mst_t ), - .mst_b_chan_t ( b_chan_slv_t ), - .slv_ar_chan_t( ar_chan_mst_t ), - .mst_ar_chan_t( ar_chan_slv_t ), - .slv_r_chan_t ( r_chan_mst_t ), - .mst_r_chan_t ( r_chan_slv_t ), - .slv_req_t ( mst_req_t ), - .slv_resp_t ( mst_resp_t ), - .mst_req_t ( slv_req_t ), - .mst_resp_t ( slv_resp_t ), - .rule_t (rule_t ) - ) i_xbar_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_i ( 1'b0 ), - .slv_ports_req_i ( masters_req ), - .slv_ports_resp_o ( masters_resp ), - .mst_ports_req_o ( slaves_req ), - .mst_ports_resp_i ( slaves_resp ), - .addr_map_i ( AddrMap ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) - ); - - // logger for master modules - for (genvar i = 0; i < NoMasters; i++) begin : gen_master_logger - axi_chan_logger #( - .TestTime ( TestTime ), // Time after clock, where sampling happens - .LoggerName( $sformatf("axi_logger_master_%0d", i)), - .aw_chan_t ( aw_chan_mst_t ), // axi AW type - .w_chan_t ( w_chan_t ), // axi W type - .b_chan_t ( b_chan_mst_t ), // axi B type - .ar_chan_t ( ar_chan_mst_t ), // axi AR type - .r_chan_t ( r_chan_mst_t ) // axi R type - ) i_mst_channel_logger ( - .clk_i ( clk ), // Clock - .rst_ni ( rst_n ), // Asynchronous reset active low, when `1'b0` no sampling - .end_sim_i ( &end_of_sim ), - // AW channel - .aw_chan_i ( masters_req[i].aw ), - .aw_valid_i ( masters_req[i].aw_valid ), - .aw_ready_i ( masters_resp[i].aw_ready ), - // W channel - .w_chan_i ( masters_req[i].w ), - .w_valid_i ( masters_req[i].w_valid ), - .w_ready_i ( masters_resp[i].w_ready ), - // B channel - .b_chan_i ( masters_resp[i].b ), - .b_valid_i ( masters_resp[i].b_valid ), - .b_ready_i ( masters_req[i].b_ready ), - // AR channel - .ar_chan_i ( masters_req[i].ar ), - .ar_valid_i ( masters_req[i].ar_valid ), - .ar_ready_i ( masters_resp[i].ar_ready ), - // R channel - .r_chan_i ( masters_resp[i].r ), - .r_valid_i ( masters_resp[i].r_valid ), - .r_ready_i ( masters_req[i].r_ready ) - ); - end - // logger for slave modules - for (genvar i = 0; i < NoSlaves; i++) begin : gen_slave_logger - axi_chan_logger #( - .TestTime ( TestTime ), // Time after clock, where sampling happens - .LoggerName( $sformatf("axi_logger_slave_%0d",i)), - .aw_chan_t ( aw_chan_slv_t ), // axi AW type - .w_chan_t ( w_chan_t ), // axi W type - .b_chan_t ( b_chan_slv_t ), // axi B type - .ar_chan_t ( ar_chan_slv_t ), // axi AR type - .r_chan_t ( r_chan_slv_t ) // axi R type - ) i_slv_channel_logger ( - .clk_i ( clk ), // Clock - .rst_ni ( rst_n ), // Asynchronous reset active low, when `1'b0` no sampling - .end_sim_i ( &end_of_sim ), - // AW channel - .aw_chan_i ( slaves_req[i].aw ), - .aw_valid_i ( slaves_req[i].aw_valid ), - .aw_ready_i ( slaves_resp[i].aw_ready ), - // W channel - .w_chan_i ( slaves_req[i].w ), - .w_valid_i ( slaves_req[i].w_valid ), - .w_ready_i ( slaves_resp[i].w_ready ), - // B channel - .b_chan_i ( slaves_resp[i].b ), - .b_valid_i ( slaves_resp[i].b_valid ), - .b_ready_i ( slaves_req[i].b_ready ), - // AR channel - .ar_chan_i ( slaves_req[i].ar ), - .ar_valid_i ( slaves_req[i].ar_valid ), - .ar_ready_i ( slaves_resp[i].ar_ready ), - // R channel - .r_chan_i ( slaves_resp[i].r ), - .r_valid_i ( slaves_resp[i].r_valid ), - .r_ready_i ( slaves_req[i].r_ready ) - ); - end - - - for (genvar i = 0; i < NoMasters; i++) begin : gen_connect_master_monitor - assign master_monitor_dv[i].aw_id = master[i].aw_id ; - assign master_monitor_dv[i].aw_addr = master[i].aw_addr ; - assign master_monitor_dv[i].aw_len = master[i].aw_len ; - assign master_monitor_dv[i].aw_size = master[i].aw_size ; - assign master_monitor_dv[i].aw_burst = master[i].aw_burst ; - assign master_monitor_dv[i].aw_lock = master[i].aw_lock ; - assign master_monitor_dv[i].aw_cache = master[i].aw_cache ; - assign master_monitor_dv[i].aw_prot = master[i].aw_prot ; - assign master_monitor_dv[i].aw_qos = master[i].aw_qos ; - assign master_monitor_dv[i].aw_region = master[i].aw_region; - assign master_monitor_dv[i].aw_atop = master[i].aw_atop ; - assign master_monitor_dv[i].aw_user = master[i].aw_user ; - assign master_monitor_dv[i].aw_valid = master[i].aw_valid ; - assign master_monitor_dv[i].aw_ready = master[i].aw_ready ; - assign master_monitor_dv[i].w_data = master[i].w_data ; - assign master_monitor_dv[i].w_strb = master[i].w_strb ; - assign master_monitor_dv[i].w_last = master[i].w_last ; - assign master_monitor_dv[i].w_user = master[i].w_user ; - assign master_monitor_dv[i].w_valid = master[i].w_valid ; - assign master_monitor_dv[i].w_ready = master[i].w_ready ; - assign master_monitor_dv[i].b_id = master[i].b_id ; - assign master_monitor_dv[i].b_resp = master[i].b_resp ; - assign master_monitor_dv[i].b_user = master[i].b_user ; - assign master_monitor_dv[i].b_valid = master[i].b_valid ; - assign master_monitor_dv[i].b_ready = master[i].b_ready ; - assign master_monitor_dv[i].ar_id = master[i].ar_id ; - assign master_monitor_dv[i].ar_addr = master[i].ar_addr ; - assign master_monitor_dv[i].ar_len = master[i].ar_len ; - assign master_monitor_dv[i].ar_size = master[i].ar_size ; - assign master_monitor_dv[i].ar_burst = master[i].ar_burst ; - assign master_monitor_dv[i].ar_lock = master[i].ar_lock ; - assign master_monitor_dv[i].ar_cache = master[i].ar_cache ; - assign master_monitor_dv[i].ar_prot = master[i].ar_prot ; - assign master_monitor_dv[i].ar_qos = master[i].ar_qos ; - assign master_monitor_dv[i].ar_region = master[i].ar_region; - assign master_monitor_dv[i].ar_user = master[i].ar_user ; - assign master_monitor_dv[i].ar_valid = master[i].ar_valid ; - assign master_monitor_dv[i].ar_ready = master[i].ar_ready ; - assign master_monitor_dv[i].r_id = master[i].r_id ; - assign master_monitor_dv[i].r_data = master[i].r_data ; - assign master_monitor_dv[i].r_resp = master[i].r_resp ; - assign master_monitor_dv[i].r_last = master[i].r_last ; - assign master_monitor_dv[i].r_user = master[i].r_user ; - assign master_monitor_dv[i].r_valid = master[i].r_valid ; - assign master_monitor_dv[i].r_ready = master[i].r_ready ; - end - for (genvar i = 0; i < NoSlaves; i++) begin : gen_connect_slave_monitor - assign slave_monitor_dv[i].aw_id = slave[i].aw_id ; - assign slave_monitor_dv[i].aw_addr = slave[i].aw_addr ; - assign slave_monitor_dv[i].aw_len = slave[i].aw_len ; - assign slave_monitor_dv[i].aw_size = slave[i].aw_size ; - assign slave_monitor_dv[i].aw_burst = slave[i].aw_burst ; - assign slave_monitor_dv[i].aw_lock = slave[i].aw_lock ; - assign slave_monitor_dv[i].aw_cache = slave[i].aw_cache ; - assign slave_monitor_dv[i].aw_prot = slave[i].aw_prot ; - assign slave_monitor_dv[i].aw_qos = slave[i].aw_qos ; - assign slave_monitor_dv[i].aw_region = slave[i].aw_region; - assign slave_monitor_dv[i].aw_atop = slave[i].aw_atop ; - assign slave_monitor_dv[i].aw_user = slave[i].aw_user ; - assign slave_monitor_dv[i].aw_valid = slave[i].aw_valid ; - assign slave_monitor_dv[i].aw_ready = slave[i].aw_ready ; - assign slave_monitor_dv[i].w_data = slave[i].w_data ; - assign slave_monitor_dv[i].w_strb = slave[i].w_strb ; - assign slave_monitor_dv[i].w_last = slave[i].w_last ; - assign slave_monitor_dv[i].w_user = slave[i].w_user ; - assign slave_monitor_dv[i].w_valid = slave[i].w_valid ; - assign slave_monitor_dv[i].w_ready = slave[i].w_ready ; - assign slave_monitor_dv[i].b_id = slave[i].b_id ; - assign slave_monitor_dv[i].b_resp = slave[i].b_resp ; - assign slave_monitor_dv[i].b_user = slave[i].b_user ; - assign slave_monitor_dv[i].b_valid = slave[i].b_valid ; - assign slave_monitor_dv[i].b_ready = slave[i].b_ready ; - assign slave_monitor_dv[i].ar_id = slave[i].ar_id ; - assign slave_monitor_dv[i].ar_addr = slave[i].ar_addr ; - assign slave_monitor_dv[i].ar_len = slave[i].ar_len ; - assign slave_monitor_dv[i].ar_size = slave[i].ar_size ; - assign slave_monitor_dv[i].ar_burst = slave[i].ar_burst ; - assign slave_monitor_dv[i].ar_lock = slave[i].ar_lock ; - assign slave_monitor_dv[i].ar_cache = slave[i].ar_cache ; - assign slave_monitor_dv[i].ar_prot = slave[i].ar_prot ; - assign slave_monitor_dv[i].ar_qos = slave[i].ar_qos ; - assign slave_monitor_dv[i].ar_region = slave[i].ar_region; - assign slave_monitor_dv[i].ar_user = slave[i].ar_user ; - assign slave_monitor_dv[i].ar_valid = slave[i].ar_valid ; - assign slave_monitor_dv[i].ar_ready = slave[i].ar_ready ; - assign slave_monitor_dv[i].r_id = slave[i].r_id ; - assign slave_monitor_dv[i].r_data = slave[i].r_data ; - assign slave_monitor_dv[i].r_resp = slave[i].r_resp ; - assign slave_monitor_dv[i].r_last = slave[i].r_last ; - assign slave_monitor_dv[i].r_user = slave[i].r_user ; - assign slave_monitor_dv[i].r_valid = slave[i].r_valid ; - assign slave_monitor_dv[i].r_ready = slave[i].r_ready ; - end -endmodule diff --git a/hw/vendor/pulp_platform_axi/test/tb_axi_xbar_pkg.sv b/hw/vendor/pulp_platform_axi/test/tb_axi_xbar_pkg.sv deleted file mode 100644 index d46a09515..000000000 --- a/hw/vendor/pulp_platform_axi/test/tb_axi_xbar_pkg.sv +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Authors: -// - Florian Zaruba -// - Wolfgang Roenninger - -// `axi_xbar_monitor` implements an AXI bus monitor that is tuned for the AXI crossbar. -// It snoops on each of the slaves and master ports of the crossbar and -// populates FIFOs and ID queues to validate that no AXI beats get -// lost or sent to the wrong destination. - -package tb_axi_xbar_pkg; - class axi_xbar_monitor #( - parameter int unsigned AxiAddrWidth, - parameter int unsigned AxiDataWidth, - parameter int unsigned AxiIdWidthMasters, - parameter int unsigned AxiIdWidthSlaves, - parameter int unsigned AxiUserWidth, - parameter int unsigned NoMasters, - parameter int unsigned NoSlaves, - parameter int unsigned NoAddrRules, - parameter type rule_t, - parameter rule_t [NoAddrRules-1:0] AddrMap, - // Stimuli application and test time - parameter time TimeTest - ); - typedef logic [AxiIdWidthMasters-1:0] mst_axi_id_t; - typedef logic [AxiIdWidthSlaves-1:0] slv_axi_id_t; - typedef logic [AxiAddrWidth-1:0] axi_addr_t; - - typedef logic [$clog2(NoMasters)-1:0] idx_mst_t; - typedef int unsigned idx_slv_t; // from rule_t - - typedef struct packed { - mst_axi_id_t mst_axi_id; - logic last; - } master_exp_t; - typedef struct packed { - slv_axi_id_t slv_axi_id; - axi_addr_t slv_axi_addr; - axi_pkg::len_t slv_axi_len; - } exp_ax_t; - typedef struct packed { - slv_axi_id_t slv_axi_id; - logic last; - } slave_exp_t; - - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t ( master_exp_t ), - .ID_WIDTH ( AxiIdWidthMasters ) - ) master_exp_queue_t; - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t ( exp_ax_t ), - .ID_WIDTH ( AxiIdWidthSlaves ) - ) ax_queue_t; - - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t ( slave_exp_t ), - .ID_WIDTH ( AxiIdWidthSlaves ) - ) slave_exp_queue_t; - - //----------------------------------------- - // Monitoring virtual interfaces - //----------------------------------------- - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) masters_axi [NoMasters-1:0]; - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slaves_axi [NoSlaves-1:0]; - //----------------------------------------- - // Queues and FIFOs to hold the expected ids - //----------------------------------------- - // Write transactions - ax_queue_t exp_aw_queue [NoSlaves-1:0]; - slave_exp_t exp_w_fifo [NoSlaves-1:0][$]; - slave_exp_t act_w_fifo [NoSlaves-1:0][$]; - master_exp_queue_t exp_b_queue [NoMasters-1:0]; - - // Read transactions - ax_queue_t exp_ar_queue [NoSlaves-1:0]; - master_exp_queue_t exp_r_queue [NoMasters-1:0]; - - //----------------------------------------- - // Bookkeeping - //----------------------------------------- - longint unsigned tests_expected; - longint unsigned tests_conducted; - longint unsigned tests_failed; - semaphore cnt_sem; - - //----------------------------------------- - // Constructor - //----------------------------------------- - function new( - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) axi_masters_vif [NoMasters-1:0], - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) axi_slaves_vif [NoSlaves-1:0] - ); - begin - this.masters_axi = axi_masters_vif; - this.slaves_axi = axi_slaves_vif; - this.tests_expected = 0; - this.tests_conducted = 0; - this.tests_failed = 0; - for (int unsigned i = 0; i < NoMasters; i++) begin - this.exp_b_queue[i] = new; - this.exp_r_queue[i] = new; - end - for (int unsigned i = 0; i < NoSlaves; i++) begin - this.exp_aw_queue[i] = new; - this.exp_ar_queue[i] = new; - end - this.cnt_sem = new(1); - end - endfunction - - // when start the testing - task cycle_start; - #TimeTest; - endtask - - // when is cycle finished - task cycle_end; - @(posedge masters_axi[0].clk_i); - endtask - - // This task monitors a slave ports of the crossbar. Every time an AW beat is seen - // it populates an id queue at the right master port (if there is no expected decode error), - // populates the expected b response in its own id_queue and in case when the atomic bit [5] - // is set it also injects an expected response in the R channel. - task automatic monitor_mst_aw(input int unsigned i); - idx_slv_t to_slave_idx; - exp_ax_t exp_aw; - slv_axi_id_t exp_aw_id; - bit decerr; - - master_exp_t exp_b; - - if (masters_axi[i].aw_valid && masters_axi[i].aw_ready) begin - // check if it should go to a decerror - decerr = 1'b1; - for (int unsigned j = 0; j < NoAddrRules; j++) begin - if ((masters_axi[i].aw_addr >= AddrMap[j].start_addr) && - (masters_axi[i].aw_addr < AddrMap[j].end_addr)) begin - to_slave_idx = idx_slv_t'(AddrMap[j].idx); - decerr = 1'b0; - end - end - // send the exp aw beat down into the queue of the slave when no decerror - if (!decerr) begin - exp_aw_id = {idx_mst_t'(i), masters_axi[i].aw_id}; - // $display("Test exp aw_id: %b",exp_aw_id); - exp_aw = '{slv_axi_id: exp_aw_id, - slv_axi_addr: masters_axi[i].aw_addr, - slv_axi_len: masters_axi[i].aw_len }; - this.exp_aw_queue[to_slave_idx].push(exp_aw_id, exp_aw); - incr_expected_tests(3); - $display("%0tns > Master %0d: AW to Slave %0d: Axi ID: %b", - $time, i, to_slave_idx, masters_axi[i].aw_id); - end else begin - $display("%0tns > Master %0d: AW to Decerror: Axi ID: %b", - $time, i, to_slave_idx, masters_axi[i].aw_id); - end - // populate the expected b queue anyway - exp_b = '{mst_axi_id: masters_axi[i].aw_id, last: 1'b1}; - this.exp_b_queue[i].push(masters_axi[i].aw_id, exp_b); - incr_expected_tests(1); - $display(" Expect B response."); - // inject expected r beats on this id, if it is an atop - if(masters_axi[i].aw_atop[5]) begin - // push the required r beats into the right fifo (reuse the exp_b variable) - $display(" Expect R response, len: %0d.", masters_axi[i].aw_len); - for (int unsigned j = 0; j <= masters_axi[i].aw_len; j++) begin - exp_b = (j == masters_axi[i].aw_len) ? - '{mst_axi_id: masters_axi[i].aw_id, last: 1'b1} : - '{mst_axi_id: masters_axi[i].aw_id, last: 1'b0}; - this.exp_r_queue[i].push(masters_axi[i].aw_id, exp_b); - incr_expected_tests(1); - end - end - end - endtask : monitor_mst_aw - - // This task monitors a slave port of the crossbar. Every time there is an AW vector it - // gets checked for its contents and if it was expected. The task then pushes an expected - // amount of W beats in the respective fifo. Emphasis of the last flag. - task automatic monitor_slv_aw(input int unsigned i); - exp_ax_t exp_aw; - slave_exp_t exp_slv_w; - // $display("%0t > Was triggered: aw_valid %b, aw_ready: %b", - // $time(), slaves_axi[i].aw_valid, slaves_axi[i].aw_ready); - if (slaves_axi[i].aw_valid && slaves_axi[i].aw_ready) begin - // test if the aw beat was expected - exp_aw = this.exp_aw_queue[i].pop_id(slaves_axi[i].aw_id); - $display("%0tns > Slave %0d: AW Axi ID: %b", - $time, i, slaves_axi[i].aw_id); - if (exp_aw.slv_axi_id != slaves_axi[i].aw_id) begin - incr_failed_tests(1); - $warning("Slave %0d: Unexpected AW with ID: %b", i, slaves_axi[i].aw_id); - end - if (exp_aw.slv_axi_addr != slaves_axi[i].aw_addr) begin - incr_failed_tests(1); - $warning("Slave %0d: Unexpected AW with ID: %b and ADDR: %h, exp: %h", - i, slaves_axi[i].aw_id, slaves_axi[i].aw_addr, exp_aw.slv_axi_addr); - end - if (exp_aw.slv_axi_len != slaves_axi[i].aw_len) begin - incr_failed_tests(1); - $warning("Slave %0d: Unexpected AW with ID: %b and LEN: %h, exp: %h", - i, slaves_axi[i].aw_id, slaves_axi[i].aw_len, exp_aw.slv_axi_len); - end - incr_conducted_tests(3); - - // push the required w beats into the right fifo - incr_expected_tests(slaves_axi[i].aw_len + 1); - for (int unsigned j = 0; j <= slaves_axi[i].aw_len; j++) begin - exp_slv_w = (j == slaves_axi[i].aw_len) ? - '{slv_axi_id: slaves_axi[i].aw_id, last: 1'b1} : - '{slv_axi_id: slaves_axi[i].aw_id, last: 1'b0}; - this.exp_w_fifo[i].push_back(exp_slv_w); - end - end - endtask : monitor_slv_aw - - // This task just pushes every W beat that gets sent on a master port in its respective fifo. - task automatic monitor_slv_w(input int unsigned i); - slave_exp_t act_slv_w; - if (slaves_axi[i].w_valid && slaves_axi[i].w_ready) begin - // $display("%0t > W beat on Slave %0d, last flag: %b", $time, i, slaves_axi[i].w_last); - act_slv_w = '{last: slaves_axi[i].w_last , default:'0}; - this.act_w_fifo[i].push_back(act_slv_w); - end - endtask : monitor_slv_w - - // This task compares the expected and actual W beats on a master port. The reason that - // this is not done in `monitor_slv_w` is that there can be per protocol W beats on the - // channel, before AW is sent to the slave. - task automatic check_slv_w(input int unsigned i); - slave_exp_t exp_w, act_w; - while (this.exp_w_fifo[i].size() != 0 && this.act_w_fifo[i].size() != 0) begin - - exp_w = this.exp_w_fifo[i].pop_front(); - act_w = this.act_w_fifo[i].pop_front(); - // do the check - incr_conducted_tests(1); - if(exp_w.last != act_w.last) begin - incr_failed_tests(1); - $warning("Slave %d: unexpected W beat last flag %b, expected: %b.", - i, act_w.last, exp_w.last); - end - end - endtask : check_slv_w - - // This task checks if a B response is allowed on a slave port of the crossbar. - task automatic monitor_mst_b(input int unsigned i); - master_exp_t exp_b; - mst_axi_id_t axi_b_id; - if (masters_axi[i].b_valid && masters_axi[i].b_ready) begin - incr_conducted_tests(1); - axi_b_id = masters_axi[i].b_id; - $display("%0tns > Master %0d: Got last B with id: %b", - $time, i, axi_b_id); - if (this.exp_b_queue[i].empty()) begin - incr_failed_tests(1); - $warning("Master %d: unexpected B beat with ID: %b detected!", i, axi_b_id); - end else begin - exp_b = this.exp_b_queue[i].pop_id(axi_b_id); - if (axi_b_id != exp_b.mst_axi_id) begin - incr_failed_tests(1); - $warning("Master: %d got unexpected B with ID: %b", i, axi_b_id); - end - end - end - endtask : monitor_mst_b - - // This task monitors the AR channel of a slave port of the crossbar. For each AR it populates - // the corresponding ID queue with the number of r beats indicated on the `ar_len` field. - // Emphasis on the last flag. We will detect reordering, if the last flags do not match, - // as each `random` burst tend to have a different length. - task automatic monitor_mst_ar(input int unsigned i); - mst_axi_id_t mst_axi_id; - axi_addr_t mst_axi_addr; - axi_pkg::len_t mst_axi_len; - - idx_slv_t exp_slv_idx; - slv_axi_id_t exp_slv_axi_id; - exp_ax_t exp_slv_ar; - master_exp_t exp_mst_r; - - logic exp_decerr; - - if (masters_axi[i].ar_valid && masters_axi[i].ar_ready) begin - exp_decerr = 1'b1; - mst_axi_id = masters_axi[i].ar_id; - mst_axi_addr = masters_axi[i].ar_addr; - mst_axi_len = masters_axi[i].ar_len; - exp_slv_axi_id = {idx_mst_t'(i), mst_axi_id}; - exp_slv_idx = '0; - for (int unsigned j = 0; j < NoAddrRules; j++) begin - if ((mst_axi_addr >= AddrMap[j].start_addr) && (mst_axi_addr < AddrMap[j].end_addr)) begin - exp_slv_idx = AddrMap[j].idx; - exp_decerr = 1'b0; - end - end - if (exp_decerr) begin - $display("%0tns > Master %0d: AR to Decerror: Axi ID: %b", - $time, i, mst_axi_id); - end else begin - $display("%0tns > Master %0d: AR to Slave %0d: Axi ID: %b", - $time, i, exp_slv_idx, mst_axi_id); - // push the expected vectors AW for exp_slv - exp_slv_ar = '{slv_axi_id: exp_slv_axi_id, - slv_axi_addr: mst_axi_addr, - slv_axi_len: mst_axi_len }; - //$display("Expected Slv Axi Id is: %b", exp_slv_axi_id); - this.exp_ar_queue[exp_slv_idx].push(exp_slv_axi_id, exp_slv_ar); - incr_expected_tests(1); - end - // push the required r beats into the right fifo - $display(" Expect R response, len: %0d.", masters_axi[i].ar_len); - for (int unsigned j = 0; j <= mst_axi_len; j++) begin - exp_mst_r = (j == mst_axi_len) ? '{mst_axi_id: mst_axi_id, last: 1'b1} : - '{mst_axi_id: mst_axi_id, last: 1'b0}; - this.exp_r_queue[i].push(mst_axi_id, exp_mst_r); - incr_expected_tests(1); - end - end - endtask : monitor_mst_ar - - // This task monitors a master port of the crossbar and checks if a transmitted AR beat was - // expected. - task automatic monitor_slv_ar(input int unsigned i); - exp_ax_t exp_slv_ar; - slv_axi_id_t slv_axi_id; - if (slaves_axi[i].ar_valid && slaves_axi[i].ar_ready) begin - incr_conducted_tests(1); - slv_axi_id = slaves_axi[i].ar_id; - if (this.exp_ar_queue[i].empty()) begin - incr_failed_tests(1); - end else begin - // check that the ids are the same - exp_slv_ar = this.exp_ar_queue[i].pop_id(slv_axi_id); - $display("%0tns > Slave %0d: AR Axi ID: %b", $time, i, slv_axi_id); - if (exp_slv_ar.slv_axi_id != slv_axi_id) begin - incr_failed_tests(1); - $warning("Slave %d: Unexpected AR with ID: %b", i, slv_axi_id); - end - end - end - endtask : monitor_slv_ar - - // This task does the R channel monitoring on a slave port. It compares the last flags, - // which are determined by the sequence of previously sent AR vectors. - task automatic monitor_mst_r(input int unsigned i); - master_exp_t exp_mst_r; - mst_axi_id_t mst_axi_r_id; - logic mst_axi_r_last; - if (masters_axi[i].r_valid && masters_axi[i].r_ready) begin - incr_conducted_tests(1); - mst_axi_r_id = masters_axi[i].r_id; - mst_axi_r_last = masters_axi[i].r_last; - if (mst_axi_r_last) begin - $display("%0tns > Master %0d: Got last R with id: %b", - $time, i, mst_axi_r_id); - end - if (this.exp_r_queue[i].empty()) begin - incr_failed_tests(1); - $warning("Master %d: unexpected R beat with ID: %b detected!", i, mst_axi_r_id); - end else begin - exp_mst_r = this.exp_r_queue[i].pop_id(mst_axi_r_id); - if (mst_axi_r_id != exp_mst_r.mst_axi_id) begin - incr_failed_tests(1); - $warning("Master: %d got unexpected R with ID: %b", i, mst_axi_r_id); - end - if (mst_axi_r_last != exp_mst_r.last) begin - incr_failed_tests(1); - $warning("Master: %d got unexpected R with ID: %b and last flag: %b", - i, mst_axi_r_id, mst_axi_r_last); - end - end - end - endtask : monitor_mst_r - - // Some tasks to manage bookkeeping of the tests conducted. - task incr_expected_tests(input int unsigned times); - cnt_sem.get(); - this.tests_expected += times; - cnt_sem.put(); - endtask : incr_expected_tests - - task incr_conducted_tests(input int unsigned times); - cnt_sem.get(); - this.tests_conducted += times; - cnt_sem.put(); - endtask : incr_conducted_tests - - task incr_failed_tests(input int unsigned times); - cnt_sem.get(); - this.tests_failed += times; - cnt_sem.put(); - endtask : incr_failed_tests - - // This task invokes the various monitoring tasks. It first forks in two, spitting - // the tasks that should continuously run and the ones that get invoked every clock cycle. - // For the tasks every clock cycle all processes that only push something in the fifo's and - // Queues get run. When they are finished the processes that pop something get run. - task run(); - Continous: fork - begin - do begin - cycle_start(); - // at every cycle span some monitoring processes - // execute all processes that put something into the queues - PushMon: fork - proc_mst_aw: begin - for (int unsigned i = 0; i < NoMasters; i++) begin - monitor_mst_aw(i); - end - end - proc_mst_ar: begin - for (int unsigned i = 0; i < NoMasters; i++) begin - monitor_mst_ar(i); - end - end - join : PushMon - // this one pops and pushes something - proc_slv_aw: begin - for (int unsigned i = 0; i < NoSlaves; i++) begin - monitor_slv_aw(i); - end - end - proc_slv_w: begin - for (int unsigned i = 0; i < NoSlaves; i++) begin - monitor_slv_w(i); - end - end - // These only pop somethong from the queses - PopMon: fork - proc_mst_b: begin - for (int unsigned i = 0; i < NoMasters; i++) begin - monitor_mst_b(i); - end - end - proc_slv_ar: begin - for (int unsigned i = 0; i < NoSlaves; i++) begin - monitor_slv_ar(i); - end - end - proc_mst_r: begin - for (int unsigned i = 0; i < NoMasters; i++) begin - monitor_mst_r(i); - end - end - join : PopMon - // check the slave W fifos last - proc_check_slv_w: begin - for (int unsigned i = 0; i < NoSlaves; i++) begin - check_slv_w(i); - end - end - cycle_end(); - end while (1'b1); - end - join - endtask : run - - task print_result(); - $info("Simulation has ended!"); - $display("Tests Expected: %d", this.tests_expected); - $display("Tests Conducted: %d", this.tests_conducted); - $display("Tests Failed: %d", this.tests_failed); - if(tests_failed > 0) begin - $error("Simulation encountered unexpected Transactions!!!!!!"); - end - endtask : print_result - endclass : axi_xbar_monitor -endpackage diff --git a/hw/vendor/pulp_platform_axi_spi_slave.core b/hw/vendor/pulp_platform_axi_spi_slave.core deleted file mode 100644 index 79f6ce14d..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave.core +++ /dev/null @@ -1,29 +0,0 @@ -CAPI=2: - -name: pulp-platform.org::axi_spi_slave - -# Copyright 2021 OpenHW Group -# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. -# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 - -filesets: - files_rtl: - depend: - - pulp-platform.org::axi:0.29.1 - files: - - pulp_platform_axi_spi_slave/spi_slave_tx.sv - - pulp_platform_axi_spi_slave/spi_slave_rx.sv - - pulp_platform_axi_spi_slave/spi_slave_syncro.sv - - pulp_platform_axi_spi_slave/spi_slave_regs.sv - - pulp_platform_axi_spi_slave/spi_slave_dc_fifo.sv - - pulp_platform_axi_spi_slave/spi_slave_cmd_parser.sv - - pulp_platform_axi_spi_slave/spi_slave_axi_plug.sv - - pulp_platform_axi_spi_slave/spi_slave_controller.sv - - pulp_platform_axi_spi_slave/axi_spi_slave.sv - - pulp_platform_axi_spi_slave/axi_spi_slave_wrap.sv - file_type: systemVerilogSource - -targets: - default: - filesets: - - files_rtl diff --git a/hw/vendor/pulp_platform_axi_spi_slave.vendor.hjson b/hw/vendor/pulp_platform_axi_spi_slave.vendor.hjson deleted file mode 100644 index de03794e9..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave.vendor.hjson +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2022 OpenHW Group -// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -{ - name: "pulp_platform_axi_spi_slave", - target_dir: "pulp_platform_axi_spi_slave", - - upstream: { - url: "https://github.com/siracusa-soc/axi_spi_slave.git", - rev: "6dd735e65edf2dfe884124a4322809c1b3d96790", - }, - -} diff --git a/hw/vendor/pulp_platform_axi_spi_slave/Bender.yml b/hw/vendor/pulp_platform_axi_spi_slave/Bender.yml deleted file mode 100644 index 4ddd82693..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/Bender.yml +++ /dev/null @@ -1,20 +0,0 @@ -package: - name: axi_sspi - -dependencies: - axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.29.1 } - -sources: - - - files: - - qspis_pkg.sv - - axi_spi_slave.sv - - spi_slave_axi_plug.sv - - spi_slave_cmd_parser.sv - - spi_slave_controller.sv - - spi_slave_dc_fifo.sv - - spi_slave_regs.sv - - spi_slave_rx.sv - - spi_slave_syncro.sv - - spi_slave_tx.sv - - axi_sspi_wrap.sv \ No newline at end of file diff --git a/hw/vendor/pulp_platform_axi_spi_slave/README.md b/hw/vendor/pulp_platform_axi_spi_slave/README.md deleted file mode 100644 index 26add64a6..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# AXI SPI Slave - -This is an implementation of a simple SPI slave. The SPI slave can be used by -an external microcontroller to access the memory of the SoC where this IP is -instantiated. The SPI slave uses the AXI bus to access the memory of the target -SoC. - -It contains dual-clock FIFOs to perform the clock domain crossing from SPI to -the SoC (AXI) domain. - -This IP depends on some PULP common cells like clock muxes, clock gates, -clock inverters and stream fork module. Those can be found in the PULP common cells repository or in -the PULPino RTL sources. The clock domain crossing functionality is reused from -the AXI slice DC component, so make sure you compile the AXI slice DC when -using this IP. diff --git a/hw/vendor/pulp_platform_axi_spi_slave/axi_spi_slave.sv b/hw/vendor/pulp_platform_axi_spi_slave/axi_spi_slave.sv deleted file mode 100644 index e92fbf2a2..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/axi_spi_slave.sv +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module axi_spi_slave -#( - parameter AXI_ADDR_WIDTH = 32, - parameter AXI_DATA_WIDTH = 64, - parameter AXI_USER_WIDTH = 6, - parameter AXI_ID_WIDTH = 3, - parameter DUMMY_CYCLES = 32 -) -( - input logic test_mode, - input logic spi_sclk, - input logic spi_cs, - output logic spi_oen0_o, - output logic spi_oen1_o, - output logic spi_oen2_o, - output logic spi_oen3_o, - input logic spi_sdi0, - input logic spi_sdi1, - input logic spi_sdi2, - input logic spi_sdi3, - output logic spi_sdo0, - output logic spi_sdo1, - output logic spi_sdo2, - output logic spi_sdo3, - - // AXI4 MASTER - //*************************************** - input logic axi_aclk, - input logic axi_aresetn, - // WRITE ADDRESS CHANNEL - output logic axi_master_aw_valid, - output logic [AXI_ADDR_WIDTH-1:0] axi_master_aw_addr, - output logic [2:0] axi_master_aw_prot, - output logic [3:0] axi_master_aw_region, - output logic [7:0] axi_master_aw_len, - output logic [2:0] axi_master_aw_size, - output logic [1:0] axi_master_aw_burst, - output logic axi_master_aw_lock, - output logic [3:0] axi_master_aw_cache, - output logic [3:0] axi_master_aw_qos, - output logic [AXI_ID_WIDTH-1:0] axi_master_aw_id, - output logic [AXI_USER_WIDTH-1:0] axi_master_aw_user, - input logic axi_master_aw_ready, - - // READ ADDRESS CHANNEL - output logic axi_master_ar_valid, - output logic [AXI_ADDR_WIDTH-1:0] axi_master_ar_addr, - output logic [2:0] axi_master_ar_prot, - output logic [3:0] axi_master_ar_region, - output logic [7:0] axi_master_ar_len, - output logic [2:0] axi_master_ar_size, - output logic [1:0] axi_master_ar_burst, - output logic axi_master_ar_lock, - output logic [3:0] axi_master_ar_cache, - output logic [3:0] axi_master_ar_qos, - output logic [AXI_ID_WIDTH-1:0] axi_master_ar_id, - output logic [AXI_USER_WIDTH-1:0] axi_master_ar_user, - input logic axi_master_ar_ready, - - // WRITE DATA CHANNEL - output logic axi_master_w_valid, - output logic [AXI_DATA_WIDTH-1:0] axi_master_w_data, - output logic [AXI_DATA_WIDTH/8-1:0] axi_master_w_strb, - output logic [AXI_USER_WIDTH-1:0] axi_master_w_user, - output logic axi_master_w_last, - input logic axi_master_w_ready, - - // READ DATA CHANNEL - input logic axi_master_r_valid, - input logic [AXI_DATA_WIDTH-1:0] axi_master_r_data, - input logic [1:0] axi_master_r_resp, - input logic axi_master_r_last, - input logic [AXI_ID_WIDTH-1:0] axi_master_r_id, - input logic [AXI_USER_WIDTH-1:0] axi_master_r_user, - output logic axi_master_r_ready, - - // WRITE RESPONSE CHANNEL - input logic axi_master_b_valid, - input logic [1:0] axi_master_b_resp, - input logic [AXI_ID_WIDTH-1:0] axi_master_b_id, - input logic [AXI_USER_WIDTH-1:0] axi_master_b_user, - output logic axi_master_b_ready -); - - logic en_quad; - logic [7:0] rx_counter; - logic rx_counter_upd; - logic [31:0] rx_data; - logic rx_data_valid; - - logic [7:0] tx_counter; - logic tx_counter_upd; - logic [31:0] tx_data; - logic tx_data_valid; - - logic ctrl_rd_wr; - - logic [31:0] ctrl_addr; - logic ctrl_addr_valid; - - logic [31:0] ctrl_data_rx; - logic ctrl_data_rx_valid; - logic ctrl_data_rx_ready; - logic [31:0] ctrl_data_tx; - logic ctrl_data_tx_valid; - logic ctrl_data_tx_ready; - - logic [31:0] fifo_data_rx; - logic fifo_data_rx_valid; - logic fifo_data_rx_ready; - logic [31:0] fifo_data_tx; - logic fifo_data_tx_valid; - logic fifo_data_tx_ready; - - logic [AXI_ADDR_WIDTH-1:0] addr_sync; - logic addr_valid_sync; - logic cs_sync; - - logic tx_done; - logic rd_wr_sync; - logic [1:0] pad_mode; - - logic [15:0] wrap_length; - - spi_slave_rx u_rxreg - ( - .sclk ( spi_sclk ), - .cs ( spi_cs ), - .sdi0 ( spi_sdi0 ), - .sdi1 ( spi_sdi1 ), - .sdi2 ( spi_sdi2 ), - .sdi3 ( spi_sdi3 ), - .en_quad_in ( en_quad ), - .counter_in ( rx_counter ), - .counter_in_upd ( rx_counter_upd ), - .data ( rx_data ), - .data_ready ( rx_data_valid ) - ); - - spi_slave_tx u_txreg - ( - .test_mode ( test_mode ), - .sclk ( spi_sclk ), - .cs ( spi_cs ), - .pad_mode ( pad_mode ), - .spi_oen0_o ( spi_oen0_o ), - .spi_oen1_o ( spi_oen1_o ), - .spi_oen2_o ( spi_oen2_o ), - .spi_oen3_o ( spi_oen3_o ), - .sdo0 ( spi_sdo0 ), - .sdo1 ( spi_sdo1 ), - .sdo2 ( spi_sdo2 ), - .sdo3 ( spi_sdo3 ), - .en_quad_in ( en_quad ), - .counter_in ( tx_counter ), - .counter_in_upd ( tx_counter_upd ), - .data ( tx_data ), - .data_valid ( tx_data_valid ), - .done ( tx_done ) - ); - - spi_slave_controller - #( - .DUMMY_CYCLES ( DUMMY_CYCLES ) - ) - u_slave_sm - ( - .sclk ( spi_sclk ), - .sys_rstn ( axi_aresetn ), - .cs ( spi_cs ), - .en_quad ( en_quad ), - .pad_mode ( pad_mode ), - .rx_counter ( rx_counter ), - .rx_counter_upd ( rx_counter_upd ), - .rx_data ( rx_data ), - .rx_data_valid ( rx_data_valid ), - .tx_counter ( tx_counter ), - .tx_counter_upd ( tx_counter_upd ), - .tx_data ( tx_data ), - .tx_data_valid ( tx_data_valid ), - .tx_done ( tx_done ), - .ctrl_rd_wr ( ctrl_rd_wr ), - .ctrl_addr ( ctrl_addr ), - .ctrl_addr_valid ( ctrl_addr_valid ), - .ctrl_data_rx ( ctrl_data_rx ), - .ctrl_data_rx_valid ( ctrl_data_rx_valid ), - .ctrl_data_rx_ready ( ctrl_data_rx_ready ), - .ctrl_data_tx ( ctrl_data_tx ), - .ctrl_data_tx_valid ( ctrl_data_tx_valid ), - .ctrl_data_tx_ready ( ctrl_data_tx_ready ), - .wrap_length ( wrap_length ) - ); - - spi_slave_dc_fifo - #( - .DATA_WIDTH ( 32 ), - .BUFFER_DEPTH ( 8 ) - ) - u_dcfifo_rx - ( - .clk_a ( spi_sclk ), - .rstn_a ( axi_aresetn ), - .data_a ( ctrl_data_rx ), - .valid_a ( ctrl_data_rx_valid ), - .ready_a ( ctrl_data_rx_ready ), - .clk_b ( axi_aclk ), - .rstn_b ( axi_aresetn ), - .data_b ( fifo_data_rx ), - .valid_b ( fifo_data_rx_valid ), - .ready_b ( fifo_data_rx_ready ) - ); - - spi_slave_dc_fifo - #( - .DATA_WIDTH ( 32 ), - .BUFFER_DEPTH ( 8 ) - ) - u_dcfifo_tx - ( - .clk_a ( axi_aclk ), - .rstn_a ( axi_aresetn ), - .data_a ( fifo_data_tx ), - .valid_a ( fifo_data_tx_valid ), - .ready_a ( fifo_data_tx_ready ), - .clk_b ( spi_sclk ), - .rstn_b ( axi_aresetn ), - .data_b ( ctrl_data_tx ), - .valid_b ( ctrl_data_tx_valid ), - .ready_b ( ctrl_data_tx_ready ) - ); - - spi_slave_axi_plug - #( - .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ), - .AXI_ID_WIDTH ( AXI_ID_WIDTH ) - ) - u_axiplug - ( - .axi_aclk ( axi_aclk ), - .axi_aresetn ( axi_aresetn ), - .axi_master_aw_valid ( axi_master_aw_valid ), - .axi_master_aw_addr ( axi_master_aw_addr ), - .axi_master_aw_prot ( axi_master_aw_prot ), - .axi_master_aw_region ( axi_master_aw_region ), - .axi_master_aw_len ( axi_master_aw_len ), - .axi_master_aw_size ( axi_master_aw_size ), - .axi_master_aw_burst ( axi_master_aw_burst ), - .axi_master_aw_lock ( axi_master_aw_lock ), - .axi_master_aw_cache ( axi_master_aw_cache ), - .axi_master_aw_qos ( axi_master_aw_qos ), - .axi_master_aw_id ( axi_master_aw_id ), - .axi_master_aw_user ( axi_master_aw_user ), - .axi_master_aw_ready ( axi_master_aw_ready ), - .axi_master_ar_valid ( axi_master_ar_valid ), - .axi_master_ar_addr ( axi_master_ar_addr ), - .axi_master_ar_prot ( axi_master_ar_prot ), - .axi_master_ar_region ( axi_master_ar_region ), - .axi_master_ar_len ( axi_master_ar_len ), - .axi_master_ar_size ( axi_master_ar_size ), - .axi_master_ar_burst ( axi_master_ar_burst ), - .axi_master_ar_lock ( axi_master_ar_lock ), - .axi_master_ar_cache ( axi_master_ar_cache ), - .axi_master_ar_qos ( axi_master_ar_qos ), - .axi_master_ar_id ( axi_master_ar_id ), - .axi_master_ar_user ( axi_master_ar_user ), - .axi_master_ar_ready ( axi_master_ar_ready ), - .axi_master_w_valid ( axi_master_w_valid ), - .axi_master_w_data ( axi_master_w_data ), - .axi_master_w_strb ( axi_master_w_strb ), - .axi_master_w_user ( axi_master_w_user ), - .axi_master_w_last ( axi_master_w_last ), - .axi_master_w_ready ( axi_master_w_ready ), - .axi_master_r_valid ( axi_master_r_valid ), - .axi_master_r_data ( axi_master_r_data ), - .axi_master_r_resp ( axi_master_r_resp ), - .axi_master_r_last ( axi_master_r_last ), - .axi_master_r_id ( axi_master_r_id ), - .axi_master_r_user ( axi_master_r_user ), - .axi_master_r_ready ( axi_master_r_ready ), - .axi_master_b_valid ( axi_master_b_valid ), - .axi_master_b_resp ( axi_master_b_resp ), - .axi_master_b_id ( axi_master_b_id ), - .axi_master_b_user ( axi_master_b_user ), - .axi_master_b_ready ( axi_master_b_ready ), - .rxtx_addr ( addr_sync ), - .rxtx_addr_valid ( addr_valid_sync ), - .start_tx ( rd_wr_sync & addr_valid_sync ), - .cs ( cs_sync ), - .tx_data ( fifo_data_tx ), - .tx_valid ( fifo_data_tx_valid ), - .tx_ready ( fifo_data_tx_ready ), - .rx_data ( fifo_data_rx ), - .rx_valid ( fifo_data_rx_valid ), - .rx_ready ( fifo_data_rx_ready ), - .wrap_length ( wrap_length ) - ); - - spi_slave_syncro - #( - .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ) - ) - u_syncro - ( - .sys_clk ( axi_aclk ), - .rstn ( axi_aresetn ), - .cs ( spi_cs ), - .address ( ctrl_addr ), - .address_valid ( ctrl_addr_valid ), - .rd_wr ( ctrl_rd_wr ), - .cs_sync ( cs_sync ), - .address_sync ( addr_sync ), - .address_valid_sync ( addr_valid_sync ), - .rd_wr_sync ( rd_wr_sync ) - ); - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/axi_spi_slave_wrap.sv b/hw/vendor/pulp_platform_axi_spi_slave/axi_spi_slave_wrap.sv deleted file mode 100644 index cf738b231..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/axi_spi_slave_wrap.sv +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - - -module axi_spi_slave_wrap -#( - parameter AXI_ADDRESS_WIDTH = 32, - parameter AXI_DATA_WIDTH = 64, - parameter AXI_ID_WIDTH = 16, - parameter AXI_USER_WIDTH = 10 - ) -( - input logic clk_i, - input logic rst_ni, - - input logic test_mode, - - AXI_BUS.Master axi_master, - - input logic spi_clk, - input logic spi_cs, - output logic spi_oen0_o, - output logic spi_oen1_o, - output logic spi_oen2_o, - output logic spi_oen3_o, - - output logic spi_sdo0, - output logic spi_sdo1, - output logic spi_sdo2, - output logic spi_sdo3, - input logic spi_sdi0, - input logic spi_sdi1, - input logic spi_sdi2, - input logic spi_sdi3 - ); - - axi_spi_slave - #( - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( AXI_ID_WIDTH ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) - ) - axi_spi_slave_i - ( - .axi_aclk ( clk_i ), - .axi_aresetn ( rst_ni ), - - .axi_master_aw_valid ( axi_master.aw_valid ), - .axi_master_aw_id ( axi_master.aw_id ), - .axi_master_aw_prot ( axi_master.aw_prot ), - .axi_master_aw_region ( axi_master.aw_region ), - .axi_master_aw_qos ( axi_master.aw_qos ), - .axi_master_aw_cache ( axi_master.aw_cache ), - .axi_master_aw_lock ( axi_master.aw_lock ), - .axi_master_aw_burst ( axi_master.aw_burst ), - .axi_master_aw_size ( axi_master.aw_size ), - .axi_master_aw_len ( axi_master.aw_len ), - .axi_master_aw_addr ( axi_master.aw_addr ), - .axi_master_aw_user ( axi_master.aw_user ), - .axi_master_aw_ready ( axi_master.aw_ready ), - - .axi_master_w_valid ( axi_master.w_valid ), - .axi_master_w_data ( axi_master.w_data ), - .axi_master_w_strb ( axi_master.w_strb ), - .axi_master_w_last ( axi_master.w_last ), - .axi_master_w_user ( axi_master.w_user ), - .axi_master_w_ready ( axi_master.w_ready ), - - .axi_master_b_valid ( axi_master.b_valid ), - .axi_master_b_id ( axi_master.b_id ), - .axi_master_b_resp ( axi_master.b_resp ), - .axi_master_b_user ( axi_master.b_user ), - .axi_master_b_ready ( axi_master.b_ready ), - - .axi_master_ar_valid ( axi_master.ar_valid ), - .axi_master_ar_id ( axi_master.ar_id ), - .axi_master_ar_prot ( axi_master.ar_prot ), - .axi_master_ar_region ( axi_master.ar_region ), - .axi_master_ar_qos ( axi_master.ar_qos ), - .axi_master_ar_cache ( axi_master.ar_cache ), - .axi_master_ar_lock ( axi_master.ar_lock ), - .axi_master_ar_burst ( axi_master.ar_burst ), - .axi_master_ar_size ( axi_master.ar_size ), - .axi_master_ar_len ( axi_master.ar_len ), - .axi_master_ar_addr ( axi_master.ar_addr ), - .axi_master_ar_user ( axi_master.ar_user ), - .axi_master_ar_ready ( axi_master.ar_ready ), - - .axi_master_r_valid ( axi_master.r_valid ), - .axi_master_r_id ( axi_master.r_id ), - .axi_master_r_data ( axi_master.r_data ), - .axi_master_r_resp ( axi_master.r_resp ), - .axi_master_r_last ( axi_master.r_last ), - .axi_master_r_user ( axi_master.r_user ), - .axi_master_r_ready ( axi_master.r_ready ), - - .test_mode ( test_mode ), - - .spi_sclk ( spi_clk ), - .spi_cs ( spi_cs ), - .spi_oen0_o ( spi_oen0_o ), - .spi_oen1_o ( spi_oen1_o ), - .spi_oen2_o ( spi_oen2_o ), - .spi_oen3_o ( spi_oen3_o ), - .spi_sdo0 ( spi_sdo0 ), - .spi_sdo1 ( spi_sdo1 ), - .spi_sdo2 ( spi_sdo2 ), - .spi_sdo3 ( spi_sdo3 ), - .spi_sdi0 ( spi_sdi0 ), - .spi_sdi1 ( spi_sdi1 ), - .spi_sdi2 ( spi_sdi2 ), - .spi_sdi3 ( spi_sdi3 ) - ); -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/axi_sspi_wrap.sv b/hw/vendor/pulp_platform_axi_spi_slave/axi_sspi_wrap.sv deleted file mode 100644 index b03f6413a..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/axi_sspi_wrap.sv +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018-2020 ETH Zurich, University of Bologna - * Copyright and related rights are licensed under the Solderpad Hardware - * License, Version 0.51 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://solderpad.org/licenses/SHL-0.51. - * - * Unless required by applicable law - * or agreed to in writing, software, hardware and materials distributed under - * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * Alfio Di Mauro - * - */ - -module axi_sspi_wrap #( - parameter AXI_ADDR_WIDTH = 32, - parameter AXI_DATA_WIDTH = 64, - parameter AXI_USER_WIDTH = 1, - parameter AXI_ID_WIDTH = 3, - parameter DUMMY_CYCLES = 1 -)( - input logic clk_i, - input logic rstn_i, - input logic test_mode_i, - - AXI_BUS.Master axi_spis_bus, - - output qspis_pkg::qspis_to_pad_t qspis_to_pad_o, - input qspis_pkg::pad_to_qspis_t pad_to_qspis_i - -); - - logic [3:0] spi_oen; - - assign qspis_to_pad_o.sd0_oe = ~spi_oen[0]; - assign qspis_to_pad_o.sd1_oe = ~spi_oen[1]; - assign qspis_to_pad_o.sd2_oe = ~spi_oen[2]; - assign qspis_to_pad_o.sd3_oe = ~spi_oen[3]; - - -axi_spi_slave #( - .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), - .AXI_DATA_WIDTH(AXI_DATA_WIDTH), - .AXI_USER_WIDTH(AXI_USER_WIDTH), - .AXI_ID_WIDTH (AXI_ID_WIDTH), - .DUMMY_CYCLES (DUMMY_CYCLES) -) i_axi_spi_slave ( - .test_mode (test_mode_i ), - - .spi_sclk (pad_to_qspis_i.sck_i ), - .spi_cs (pad_to_qspis_i.csn_i ), - - .spi_oen0_o (spi_oen[0] ), - .spi_oen1_o (spi_oen[1] ), - .spi_oen2_o (spi_oen[2] ), - .spi_oen3_o (spi_oen[3] ), - - - .spi_sdi0 (pad_to_qspis_i.sd0_i ), - .spi_sdi1 (pad_to_qspis_i.sd1_i ), - .spi_sdi2 (pad_to_qspis_i.sd2_i ), - .spi_sdi3 (pad_to_qspis_i.sd3_i ), - .spi_sdo0 (qspis_to_pad_o.sd0_o ), - .spi_sdo1 (qspis_to_pad_o.sd1_o ), - .spi_sdo2 (qspis_to_pad_o.sd2_o ), - .spi_sdo3 (qspis_to_pad_o.sd3_o ), - - .axi_aclk (clk_i ), - .axi_aresetn (rstn_i ), - - .axi_master_aw_valid (axi_spis_bus.aw_valid ), - .axi_master_aw_addr (axi_spis_bus.aw_addr ), - .axi_master_aw_prot (axi_spis_bus.aw_prot ), - .axi_master_aw_region(axi_spis_bus.aw_region), - .axi_master_aw_len (axi_spis_bus.aw_len ), - .axi_master_aw_size (axi_spis_bus.aw_size ), - .axi_master_aw_burst (axi_spis_bus.aw_burst ), - .axi_master_aw_lock (axi_spis_bus.aw_lock ), - .axi_master_aw_cache (axi_spis_bus.aw_cache ), - .axi_master_aw_qos (axi_spis_bus.aw_qos ), - .axi_master_aw_id (axi_spis_bus.aw_id ), - .axi_master_aw_user (axi_spis_bus.aw_user ), - .axi_master_aw_ready (axi_spis_bus.aw_ready ), - .axi_master_ar_valid (axi_spis_bus.ar_valid ), - .axi_master_ar_addr (axi_spis_bus.ar_addr ), - .axi_master_ar_prot (axi_spis_bus.ar_prot ), - .axi_master_ar_region(axi_spis_bus.ar_region), - .axi_master_ar_len (axi_spis_bus.ar_len ), - .axi_master_ar_size (axi_spis_bus.ar_size ), - .axi_master_ar_burst (axi_spis_bus.ar_burst ), - .axi_master_ar_lock (axi_spis_bus.ar_lock ), - .axi_master_ar_cache (axi_spis_bus.ar_cache ), - .axi_master_ar_qos (axi_spis_bus.ar_qos ), - .axi_master_ar_id (axi_spis_bus.ar_id ), - .axi_master_ar_user (axi_spis_bus.ar_user ), - .axi_master_ar_ready (axi_spis_bus.ar_ready ), - .axi_master_w_valid (axi_spis_bus.w_valid ), - .axi_master_w_data (axi_spis_bus.w_data ), - .axi_master_w_strb (axi_spis_bus.w_strb ), - .axi_master_w_user (axi_spis_bus.w_user ), - .axi_master_w_last (axi_spis_bus.w_last ), - .axi_master_w_ready (axi_spis_bus.w_ready ), - .axi_master_r_valid (axi_spis_bus.r_valid ), - .axi_master_r_data (axi_spis_bus.r_data ), - .axi_master_r_resp (axi_spis_bus.r_resp ), - .axi_master_r_last (axi_spis_bus.r_last ), - .axi_master_r_id (axi_spis_bus.r_id ), - .axi_master_r_user (axi_spis_bus.r_user ), - .axi_master_r_ready (axi_spis_bus.r_ready ), - .axi_master_b_valid (axi_spis_bus.b_valid ), - .axi_master_b_resp (axi_spis_bus.b_resp ), - .axi_master_b_id (axi_spis_bus.b_id ), - .axi_master_b_user (axi_spis_bus.b_user ), - .axi_master_b_ready (axi_spis_bus.b_ready ) -); - - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/qspis_pkg.sv b/hw/vendor/pulp_platform_axi_spi_slave/qspis_pkg.sv deleted file mode 100644 index 07986eb47..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/qspis_pkg.sv +++ /dev/null @@ -1,21 +0,0 @@ -package qspis_pkg; - // qspis structure - typedef struct packed { - logic sd0_o; - logic sd0_oe; - logic sd1_o; - logic sd1_oe; - logic sd2_o; - logic sd2_oe; - logic sd3_o; - logic sd3_oe; - } qspis_to_pad_t; - typedef struct packed { - logic sd0_i; - logic sd1_i; - logic sd2_i; - logic sd3_i; - logic csn_i; - logic sck_i; - } pad_to_qspis_t; -endpackage \ No newline at end of file diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_axi_plug.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_axi_plug.sv deleted file mode 100644 index f8e683b41..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_axi_plug.sv +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module spi_slave_axi_plug -#( - parameter AXI_ADDR_WIDTH = 32, - parameter AXI_DATA_WIDTH = 64, - parameter AXI_USER_WIDTH = 6, - parameter AXI_ID_WIDTH = 3 -) -( - // AXI4 MASTER - //*************************************** - input logic axi_aclk, - input logic axi_aresetn, - // WRITE ADDRESS CHANNEL - output logic axi_master_aw_valid, - output logic [AXI_ADDR_WIDTH-1:0] axi_master_aw_addr, - output logic [2:0] axi_master_aw_prot, - output logic [3:0] axi_master_aw_region, - output logic [7:0] axi_master_aw_len, - output logic [2:0] axi_master_aw_size, - output logic [1:0] axi_master_aw_burst, - output logic axi_master_aw_lock, - output logic [3:0] axi_master_aw_cache, - output logic [3:0] axi_master_aw_qos, - output logic [AXI_ID_WIDTH-1:0] axi_master_aw_id, - output logic [AXI_USER_WIDTH-1:0] axi_master_aw_user, - input logic axi_master_aw_ready, - - // READ ADDRESS CHANNEL - output logic axi_master_ar_valid, - output logic [AXI_ADDR_WIDTH-1:0] axi_master_ar_addr, - output logic [2:0] axi_master_ar_prot, - output logic [3:0] axi_master_ar_region, - output logic [7:0] axi_master_ar_len, - output logic [2:0] axi_master_ar_size, - output logic [1:0] axi_master_ar_burst, - output logic axi_master_ar_lock, - output logic [3:0] axi_master_ar_cache, - output logic [3:0] axi_master_ar_qos, - output logic [AXI_ID_WIDTH-1:0] axi_master_ar_id, - output logic [AXI_USER_WIDTH-1:0] axi_master_ar_user, - input logic axi_master_ar_ready, - - // WRITE DATA CHANNEL - output logic axi_master_w_valid, - output logic [AXI_DATA_WIDTH-1:0] axi_master_w_data, - output logic [AXI_DATA_WIDTH/8-1:0] axi_master_w_strb, - output logic [AXI_USER_WIDTH-1:0] axi_master_w_user, - output logic axi_master_w_last, - input logic axi_master_w_ready, - - // READ DATA CHANNEL - input logic axi_master_r_valid, - input logic [AXI_DATA_WIDTH-1:0] axi_master_r_data, - input logic [1:0] axi_master_r_resp, - input logic axi_master_r_last, - input logic [AXI_ID_WIDTH-1:0] axi_master_r_id, - input logic [AXI_USER_WIDTH-1:0] axi_master_r_user, - output logic axi_master_r_ready, - - // WRITE RESPONSE CHANNEL - input logic axi_master_b_valid, - input logic [1:0] axi_master_b_resp, - input logic [AXI_ID_WIDTH-1:0] axi_master_b_id, - input logic [AXI_USER_WIDTH-1:0] axi_master_b_user, - output logic axi_master_b_ready, - - input logic [AXI_ADDR_WIDTH-1:0] rxtx_addr, - input logic rxtx_addr_valid, - input logic start_tx, - input logic cs, - output logic [31:0] tx_data, - output logic tx_valid, - input logic tx_ready, - input logic [31:0] rx_data, - input logic rx_valid, - output logic rx_ready, - - input logic [15:0] wrap_length - ); - - logic [AXI_ADDR_WIDTH-1:0] curr_addr; - logic [AXI_ADDR_WIDTH-1:0] next_addr; - logic [31:0] curr_data_rx; - logic [AXI_DATA_WIDTH-1:0] curr_data_tx; - logic incr_addr_w; - logic incr_addr_r; - logic sample_fifo; - logic sample_axidata; - logic axi_int_w_ready; - logic axi_int_w_valid; - - // up to 64 kwords (256kB) - logic [15:0] tx_counter; - - enum logic [2:0] {Idle, Data, AxiValid, AxiAddr, AxiData, AxiResp} ar_q, ar_d, aw_q, aw_d; - always_ff @(posedge axi_aclk or negedge axi_aresetn) - begin - if (axi_aresetn == 0) - begin - aw_q <= Idle; - ar_q <= Idle; - curr_data_rx <= 'h0; - curr_data_tx <= 'h0; - curr_addr <= 'h0; - end - else - begin - aw_q <= aw_d; - ar_q <= ar_d; - if (sample_fifo) - begin - curr_data_rx <= rx_data; - end - if (sample_axidata) - curr_data_tx <= axi_master_r_data; - if (rxtx_addr_valid) - curr_addr <= rxtx_addr; - else if (incr_addr_w | incr_addr_r) - curr_addr <= next_addr; - end - end - - always_ff @(posedge axi_aclk or negedge axi_aresetn) - begin - if (axi_aresetn == 1'b0) - tx_counter <= 16'h0; - else if(start_tx) - tx_counter <= 16'h0; - else if(incr_addr_w | incr_addr_r) begin - if(tx_counter == wrap_length-1) - tx_counter <= 16'h0; - else - tx_counter <= tx_counter + 16'h1; - end - end - - always_comb - begin - next_addr = 32'b0; - if(rxtx_addr_valid) - next_addr = rxtx_addr; - else if(tx_counter == wrap_length-1) - next_addr = rxtx_addr; - else - next_addr = curr_addr + 32'h4; - end - - // "stream_fork" module is used to decouple AW and W channels - stream_fork #( - .N_OUP (2) - ) stream_fork_i ( - .clk_i ( axi_aclk ), - .rst_ni ( axi_aresetn ), - .valid_i ( axi_int_w_valid ), - .ready_o ( axi_int_w_ready ), - .valid_o ( {axi_master_w_valid, axi_master_aw_valid} ), - .ready_i ( {axi_master_w_ready, axi_master_aw_ready} ) - ); - - always_comb - begin - aw_d = Idle; - sample_fifo = 1'b0; - rx_ready = 1'b0; - axi_master_b_ready = 1'b0; - axi_int_w_valid = 1'b0; - incr_addr_w = 1'b0; - unique case(aw_q) - Idle: - begin - if(rx_valid) - begin - sample_fifo = 1'b1; - rx_ready = 1'b1; - aw_d = AxiValid; - end - else - begin - aw_d = Idle; - end - end // case: Idle - AxiValid: - begin - axi_int_w_valid = 1'b1; - if(axi_int_w_ready) - begin - incr_addr_w = 1'b1; - aw_d = AxiResp; - end - else - begin - aw_d = AxiValid; - end - end - AxiResp: - begin - axi_master_b_ready = 1'b1; - if (axi_master_b_valid) - aw_d = Idle; - else - aw_d = AxiResp; - end - - endcase - end - - always_comb - begin - ar_d = Idle; - tx_valid = 1'b0; - axi_master_ar_valid = 1'b0; - axi_master_r_ready = 1'b0; - incr_addr_r = 1'b0; - sample_axidata = 1'b0; - case(ar_q) - Idle: - begin - if(start_tx && !cs) - begin - ar_d = AxiAddr; - end - else - begin - ar_d = Idle; - end - end - Data: - begin - tx_valid = 1'b1; - if (cs) - begin - ar_d = Idle; - end - else - begin - if(tx_ready) - if(tx_counter == wrap_length-1) - begin - ar_d = Idle; - end - else - begin - incr_addr_r = 1'b1; - ar_d = AxiAddr; - end - else - begin - ar_d = Data; - end - end - end - AxiAddr: - begin - axi_master_ar_valid = 1'b1; - if (axi_master_ar_ready) - ar_d = AxiResp; - else - ar_d = AxiAddr; - end - AxiResp: - begin - axi_master_r_ready = 1'b1; - if (axi_master_r_valid) - begin - sample_axidata = 1'b1; - ar_d = Data; - end - else - ar_d = AxiResp; - end - - endcase - end - - // for now, let us support only 32-bit reads! - generate if (AXI_DATA_WIDTH == 32) - assign tx_data = curr_data_tx[31:0]; - else - assign tx_data = curr_addr[2] ? curr_data_tx[63:32] : curr_data_tx[31:0]; - endgenerate - - assign axi_master_aw_addr = curr_addr; - assign axi_master_aw_prot = 'h0; - assign axi_master_aw_region = 'h0; - assign axi_master_aw_len = 'h0; - assign axi_master_aw_size = 3'b010; - assign axi_master_aw_burst = 'h0; - assign axi_master_aw_lock = 'h0; - assign axi_master_aw_cache = 'h0; - assign axi_master_aw_qos = 'h0; - assign axi_master_aw_id = 'h1; - assign axi_master_aw_user = 'h0; - - assign axi_master_w_data = {AXI_DATA_WIDTH/32{curr_data_rx}}; // replicate curr_data_rx as often as needed - generate if (AXI_DATA_WIDTH == 32) - assign axi_master_w_strb = 4'hF; - else - assign axi_master_w_strb = curr_addr[2] ? 8'hF0 : 8'h0F; - endgenerate - assign axi_master_w_user = 'h0; - assign axi_master_w_last = 1'b1; - - assign axi_master_ar_addr = curr_addr; - assign axi_master_ar_prot = 'h0; - assign axi_master_ar_region = 'h0; - assign axi_master_ar_len = 'h0; - assign axi_master_ar_size = 3'b010; - assign axi_master_ar_burst = 'h0; - assign axi_master_ar_lock = 'h0; - assign axi_master_ar_cache = 'h0; - assign axi_master_ar_qos = 'h0; - assign axi_master_ar_id = 'h1; - assign axi_master_ar_user = 'h0; - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_cmd_parser.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_cmd_parser.sv deleted file mode 100644 index 432a230de..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_cmd_parser.sv +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module spi_slave_cmd_parser( - input logic [7:0] cmd, - output logic get_addr, - output logic get_mode, - output logic get_data, - output logic send_data, - output logic enable_cont, - output logic enable_regs, - output logic wait_dummy, - output logic error, - output logic [1:0] reg_sel - ); - - - always_comb - begin - get_addr = 0; - get_mode = 0; - get_data = 0; - send_data = 0; - enable_cont = 0; - enable_regs = 1'b0; - wait_dummy = 0; - reg_sel = 2'b00; - error = 1'b1; - case(cmd) - 8'h1: //write reg0 - begin - get_addr = 0; - get_mode = 0; - get_data = 1; - send_data = 0; - enable_cont = 0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 0; - reg_sel = 2'b00; - end - 8'h2: //write mem - begin - get_addr = 1; - get_mode = 0; - get_data = 1; - send_data = 0; - enable_cont = 1'b1; - enable_regs = 1'b0; - error = 1'b0; - wait_dummy = 0; - reg_sel = 2'b00; - end - 8'h5: //read reg0 - begin - get_addr = 0; - get_mode = 0; - get_data = 0; - send_data = 1; - enable_cont = 0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 0; - reg_sel = 2'b00; - end - 8'h7: //read reg1 - begin - get_addr = 0; - get_mode = 0; - get_data = 0; - send_data = 1; - enable_cont = 0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 0; - reg_sel = 2'b01; - end - 8'hB: //read mem - begin - get_addr = 1; - get_mode = 0; - get_data = 0; - send_data = 1; - enable_cont = 1'b1; - enable_regs = 1'b0; - error = 1'b0; - wait_dummy = 1; - reg_sel = 2'b00; - end - 8'h11: //write reg1 - begin - get_addr = 1'b0; - get_mode = 1'b0; - get_data = 1'b1; - send_data = 1'b0; - enable_cont = 1'b0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 1'b0; - reg_sel = 2'b01; - end - 8'h20: // write reg2 - begin - get_addr = 1'b0; - get_mode = 1'b0; - get_data = 1'b1; - send_data = 1'b0; - enable_cont = 1'b0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 1'b0; - reg_sel = 2'b10; - end - 8'h21: // read reg2 - begin - get_addr = 1'b0; - get_mode = 1'b0; - get_data = 1'b0; - send_data = 1'b1; - enable_cont = 1'b0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 1'b0; - reg_sel = 2'b10; - end - 8'h30: // write reg3 - begin - get_addr = 1'b0; - get_mode = 1'b0; - get_data = 1'b1; - send_data = 1'b0; - enable_cont = 1'b0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 1'b0; - reg_sel = 2'b11; - end - 8'h31: // read reg3 - begin - get_addr = 1'b0; - get_mode = 1'b0; - get_data = 1'b0; - send_data = 1'b1; - enable_cont = 1'b0; - enable_regs = 1'b1; - error = 1'b0; - wait_dummy = 1'b0; - reg_sel = 2'b11; - end - endcase - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_controller.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_controller.sv deleted file mode 100644 index da6bb90ee..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_controller.sv +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -`define SPI_STD_TX 2'b00 -`define SPI_STD_RX 2'b01 -`define SPI_QUAD_TX 2'b10 -`define SPI_QUAD_RX 2'b11 - -module spi_slave_controller - #( - parameter DUMMY_CYCLES = 32 - ) - (input logic sclk, - input logic sys_rstn, - input logic cs, - output logic en_quad, - output logic [1:0] pad_mode, - output logic [7:0] rx_counter, - output logic rx_counter_upd, - input logic [31:0] rx_data, - input logic rx_data_valid, - output logic [7:0] tx_counter, - output logic tx_counter_upd, - output logic [31:0] tx_data, - output logic tx_data_valid, - input logic tx_done, - output logic ctrl_rd_wr, - output logic [31:0] ctrl_addr, - output logic ctrl_addr_valid, - output logic [31:0] ctrl_data_rx, - output logic ctrl_data_rx_valid, - input logic ctrl_data_rx_ready, - input logic [31:0] ctrl_data_tx, - input logic ctrl_data_tx_valid, - output logic ctrl_data_tx_ready, - output logic [15:0] wrap_length - ); - - localparam REG_SIZE = 8; - - - enum logic [2:0] {CMD,ADDR,MODE,DATA_TX,DATA_RX,DUMMY,ERROR} state,state_next; - - logic [7:0] command; - - logic decode_cmd_comb; - - logic [31:0] addr_reg; - logic [7:0] cmd_reg; - logic [7:0] mode_reg; - logic [31:0] data_reg; - - logic sample_ADDR; - logic sample_MODE; - logic sample_CMD; - logic sample_DATA; - - logic get_addr; - logic wait_dummy; - logic get_mode; - logic get_data; - logic send_data; - logic enable_cont; - logic enable_regs; - logic cmd_error; - logic [1:0] reg_sel; - logic [REG_SIZE-1:0] reg_data; - logic reg_valid; - - logic ctrl_data_tx_ready_next; - logic [7:0] tx_counter_next; - logic tx_counter_upd_next; - logic tx_data_valid_next; - logic tx_done_reg; - - logic [7:0] s_dummy_cycles; - - assign command = decode_cmd_comb ? rx_data[7:0] : cmd_reg; - - spi_slave_cmd_parser u_cmd_parser - ( - .cmd ( command ), // In, - .get_addr ( get_addr ), // Out, - .get_mode ( get_mode ), // Out, - .get_data ( get_data ), // Out, - .send_data ( send_data ), // Out, - .wait_dummy ( wait_dummy ), // Out, - .enable_cont ( enable_cont ), // Out, - .enable_regs ( enable_regs ), // Out, - .error ( cmd_error ), // Out, - .reg_sel ( reg_sel ) // Out - ); - - spi_slave_regs #( - .REG_SIZE(REG_SIZE) - ) u_spiregs( - .sclk(sclk), - .rstn(sys_rstn), - .wr_data(rx_data[REG_SIZE-1:0]), - .wr_addr(reg_sel), - .wr_data_valid(reg_valid), - .rd_data(reg_data), - .rd_addr(reg_sel), - .dummy_cycles(s_dummy_cycles), - .en_qpi(en_quad), - .wrap_length(wrap_length) - ); - always_comb - begin - pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD_RX; - rx_counter = 8'h1F; - rx_counter_upd = 0; - tx_counter_next = 8'h1F; - tx_counter_upd_next = 0; - decode_cmd_comb = 1'b0; - sample_ADDR = 1'b0; - sample_MODE = 1'b0; - sample_CMD = 1'b0; - sample_DATA = 1'b0; - ctrl_data_rx_valid = 1'b0; - ctrl_data_tx_ready_next = 1'b0; - reg_valid = 1'b0; - tx_data_valid_next = 1'b0; - state_next = state; - case(state) - CMD: - begin - pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD_RX; - decode_cmd_comb = 1'b1; - ctrl_data_tx_ready_next = 1'b1; //empty TX fifo if not allready empty - if(rx_data_valid) - begin - sample_CMD = 1'b1; - if (get_addr) - begin - state_next = ADDR; - rx_counter_upd = 1; - rx_counter = en_quad ? 8'h7 : 8'h1F; - end - else if (get_data) - begin - state_next = DATA_RX; - rx_counter_upd = 1; - if (enable_regs) - rx_counter = en_quad ? 8'h1 : 8'h7; - end - else - begin - state_next = DATA_TX; - tx_counter_upd_next = 1; - tx_data_valid_next = 1'b1; - tx_counter_next = en_quad ? 8'h7 : 8'h1F; - if (~enable_regs) - ctrl_data_tx_ready_next = 1'b1; - end - end - else - begin - state_next = CMD; - end - end - ADDR: - begin - pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD_RX; - ctrl_data_tx_ready_next = 1'b1; - if(rx_data_valid) - begin - sample_ADDR = 1'b1; - if (wait_dummy) - begin - state_next = DUMMY; - rx_counter = s_dummy_cycles; - rx_counter_upd = 1; - end - else if (send_data) - begin - state_next = DATA_TX; - tx_counter_upd_next = 1; - tx_counter_next = en_quad ? 8'h7 : 8'h1F; - end - else if (get_data) - begin - state_next = DATA_RX; - rx_counter_upd = 1; - rx_counter = en_quad ? 8'h7 : 8'h1F; - end - end - else - begin - state_next = ADDR; - end - end - MODE: - begin - pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD_RX; - if(rx_data_valid) - begin - if (wait_dummy) - begin - state_next = DUMMY; - rx_counter = DUMMY_CYCLES; - rx_counter_upd = 1; - end - else if (get_data) - begin - state_next = DATA_RX; - rx_counter = en_quad ? 8'h7 : 8'h1F; - rx_counter_upd = 1; - end - else if (send_data) - begin - state_next = DATA_TX; - tx_counter_next = en_quad ? 8'h7 : 8'h1F; - tx_counter_upd_next = 1; - tx_data_valid_next = 1'b1; - if (~enable_regs) - ctrl_data_tx_ready_next = 1'b1; - end - end - else - begin - state_next = MODE; - end - end - DUMMY: - begin - pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD_RX; - if(rx_data_valid) - begin - if (get_data) - begin - state_next = DATA_RX; - rx_counter = en_quad ? 8'h7 : 8'h1F; - rx_counter_upd = 1; - end - else - begin - state_next = DATA_TX; - tx_counter_next = en_quad ? 8'h7 : 8'h1F; - tx_counter_upd_next = 1; - tx_data_valid_next = 1'b1; - if (~enable_regs) - ctrl_data_tx_ready_next = 1'b1; - end - end - else - begin - state_next = DUMMY; - end - end - DATA_RX: - begin - pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD_RX; - if(rx_data_valid) - begin - if (enable_regs) - reg_valid = 1'b1; - else - ctrl_data_rx_valid = 1'b1; - if (enable_cont) - begin - state_next = DATA_RX; - rx_counter = en_quad ? 8'h7 : 8'h1F; - rx_counter_upd = 1; - end - else - begin - state_next = CMD; - rx_counter = en_quad ? 8'h1 : 8'h7; - rx_counter_upd = 1; - end - end - else - begin - state_next = DATA_RX; - end - end - DATA_TX: - begin - pad_mode = en_quad ? `SPI_QUAD_TX : `SPI_STD_TX; - if(tx_done_reg) - begin - if (enable_cont) - begin - state_next = DATA_TX; - tx_counter_next = en_quad ? 8'h7 : 8'h1F; - tx_counter_upd_next = 1; - tx_data_valid_next = 1'b1; - if (~enable_regs) - ctrl_data_tx_ready_next = 1'b1; - end - else - begin - state_next = CMD; - rx_counter = en_quad ? 8'h1 : 8'h7; - rx_counter_upd = 1; - end - end - else - begin - state_next = DATA_TX; - end - end - ERROR: - begin - state_next = ERROR; - end - endcase - end - - - always @(posedge sclk or posedge cs) - begin - if (cs == 1'b1) - begin - state <= CMD; - end - else - begin - state <= state_next; - end - end - - always @(posedge sclk or posedge cs) - begin - if (cs == 1'b1) - begin - addr_reg <= 'h0; - mode_reg <= 'h0; - data_reg <= 'h0; - cmd_reg <= 'h0; - tx_done_reg <= 1'b0; - ctrl_addr_valid <= 1'b0; - tx_counter_upd <= 1'b0; - tx_data_valid <= 1'b0; - ctrl_data_tx_ready <= 1'b0; - tx_counter <= 'h0; - tx_data <= 'h0; - end - else - begin - if (sample_ADDR) addr_reg <= rx_data; - if (sample_MODE) mode_reg <= rx_data[7:0]; - if (sample_CMD) cmd_reg <= rx_data[7:0]; - if (sample_DATA) data_reg <= rx_data; - ctrl_addr_valid <= sample_ADDR; - tx_counter_upd <= tx_counter_upd_next; - tx_counter <= tx_counter_next; - tx_data_valid <= tx_data_valid_next; - tx_done_reg <= tx_done; - ctrl_data_tx_ready <= ctrl_data_tx_ready_next; - tx_data <= (enable_regs) ? reg_data : ctrl_data_tx; - end - end - - assign ctrl_data_rx = rx_data; - assign ctrl_addr = addr_reg; - assign ctrl_rd_wr = send_data; - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_dc_fifo.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_dc_fifo.sv deleted file mode 100644 index c20b8910e..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_dc_fifo.sv +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module spi_slave_dc_fifo #( - parameter DATA_WIDTH = 32, - parameter BUFFER_DEPTH = 8 -)( - input logic clk_a, - input logic rstn_a, - input logic [DATA_WIDTH-1:0] data_a, - input logic valid_a, - output logic ready_a, - input logic clk_b, - input logic rstn_b, - output logic [DATA_WIDTH-1:0] data_b, - output logic valid_b, - input logic ready_b -); - - typedef logic [DATA_WIDTH-1:0] data_t; - - cdc_fifo_gray #(.WIDTH(32), .T(data_t), .LOG_DEPTH(4), .SYNC_STAGES(2)) i_cdc_fifo_gray ( - - .src_rst_ni (rstn_a ), - .src_clk_i (clk_a ), - .src_data_i (data_a ), - .src_valid_i(valid_a), - .src_ready_o(ready_a), - - .dst_rst_ni (rstn_b ), - .dst_clk_i (clk_b ), - .dst_data_o (data_b ), - .dst_valid_o(valid_b), - .dst_ready_i(ready_b) - - ); - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_regs.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_regs.sv deleted file mode 100644 index b3f415d7e..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_regs.sv +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module spi_slave_regs #( - parameter REG_SIZE = 8 -) ( - input logic sclk, - input logic rstn, - input logic [REG_SIZE-1:0] wr_data, - input logic [1:0] wr_addr, - input logic wr_data_valid, - output logic [REG_SIZE-1:0] rd_data, - input logic [1:0] rd_addr, - output logic [7:0] dummy_cycles, - output logic en_qpi, - output logic [15:0] wrap_length -); - - logic [REG_SIZE-1:0] reg0; // bit 0 enables qpi - logic [REG_SIZE-1:0] reg1; // number of dummy cycles - logic [REG_SIZE-1:0] reg2; // wrap length, low - logic [REG_SIZE-1:0] reg3; // wrap length, high - - assign en_qpi = reg0[0]; - assign dummy_cycles = reg1; - assign wrap_length = {reg3,reg2}; - - always_comb - begin - case(rd_addr) - 2'b00: - rd_data = reg0; - 2'b01: - rd_data = reg1; - 2'b10: - rd_data = reg2; - 2'b11: - rd_data = reg3; - endcase - end - - always @(posedge sclk or negedge rstn) - begin - if (rstn == 0) - begin - reg0 <= 'h0; - reg1 <= 'd32; - reg2 <= 'h0; - reg3 <= 'h0; - end - else - begin - if (wr_data_valid) - begin - case(wr_addr) - 2'b00: - reg0 <= wr_data; - 2'b01: - reg1 <= wr_data; - 2'b10: - reg2 <= wr_data; - 2'b11: - reg3 <= wr_data; - endcase - end - end - end -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_rx.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_rx.sv deleted file mode 100644 index f0c2a7b66..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_rx.sv +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module spi_slave_rx -( - input logic sclk, - input logic cs, - input logic sdi0, - input logic sdi1, - input logic sdi2, - input logic sdi3, - input logic en_quad_in, - input logic [7:0] counter_in, - input logic counter_in_upd, - output logic [31:0] data, - output logic data_ready -); - - reg [31:0] data_int; - reg [31:0] data_int_next; - reg [7:0] counter; - reg [7:0] counter_trgt; - reg [7:0] counter_next; - reg [7:0] counter_trgt_next; - - logic running; - logic running_next; - - assign data = data_int_next; - - always_comb - begin - if (counter_in_upd) - counter_trgt_next = counter_in; - else if ((counter_trgt == 8'h1) && !en_quad_in) - counter_trgt_next = 8'h7; - else - counter_trgt_next = counter_trgt; - - if (counter_in_upd) - running_next = 1'b1; - else if (counter == counter_trgt) - running_next = 1'b0; - else - running_next = running; - - if (running) - begin - if (counter == counter_trgt) - begin - counter_next = 'h0; - data_ready = 1'b1; - end - else - begin - counter_next = counter + 1; - data_ready = 1'b0; - end - if (en_quad_in) - data_int_next = {data_int[27:0],sdi3,sdi2,sdi1,sdi0}; - else - data_int_next = {data_int[30:0],sdi0}; - end - else - begin - counter_next = counter; - data_ready = 1'b0; - data_int_next = data_int; - end - end - - - always @(posedge sclk or posedge cs) - begin - if (cs == 1'b1) - begin - counter <= 0; - counter_trgt <= 'h1; - data_int <= 'h0; - running <= 'h1; - end - else - begin - counter <= counter_next; - counter_trgt <= counter_trgt_next; - data_int <= data_int_next; - running <= running_next; - end - end -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_syncro.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_syncro.sv deleted file mode 100644 index 35fd19ffb..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_syncro.sv +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -module spi_slave_syncro - #( - parameter AXI_ADDR_WIDTH = 32 - ) - ( - input logic sys_clk, - input logic rstn, - input logic cs, - input logic [AXI_ADDR_WIDTH-1:0] address, - input logic address_valid, - input logic rd_wr, - output logic cs_sync, - output logic [AXI_ADDR_WIDTH-1:0] address_sync, - output logic address_valid_sync, - output logic rd_wr_sync - ); - - logic [1:0] cs_reg; - logic [2:0] valid_reg; - logic [1:0] rdwr_reg; - - assign cs_sync = cs_reg[1]; - assign address_valid_sync = ~valid_reg[2] & valid_reg[1]; //detect rising edge of addr valid - assign address_sync = address; - assign rd_wr_sync = rdwr_reg[1]; - - always @(posedge sys_clk or negedge rstn) - begin - if(rstn == 1'b0) - begin - cs_reg <= 2'b11; - valid_reg <= 3'b000; - rdwr_reg <= 2'b00; - end - else - begin - cs_reg <= {cs_reg[0],cs}; - valid_reg <= {valid_reg[1:0],address_valid}; - rdwr_reg <= {rdwr_reg[0],rd_wr}; - end - end - -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_tx.sv b/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_tx.sv deleted file mode 100644 index b3df84e90..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/spi_slave_tx.sv +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2017 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the “License”); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -`define SPI_STD_TX 2'b00 -`define SPI_STD_RX 2'b01 -`define SPI_QUAD_TX 2'b10 -`define SPI_QUAD_RX 2'b11 - -module spi_slave_tx -( - input logic test_mode, - input logic sclk, - input logic cs, - input logic [1:0] pad_mode, - output logic spi_oen0_o, - output logic spi_oen1_o, - output logic spi_oen2_o, - output logic spi_oen3_o, - output logic sdo0, - output logic sdo1, - output logic sdo2, - output logic sdo3, - input logic en_quad_in, - input logic [7:0] counter_in, - input logic counter_in_upd, - input logic [31:0] data, - input logic data_valid, - output logic done -); - - reg [31:0] data_int; - reg [31:0] data_int_next; - reg [7:0] counter; - reg [7:0] counter_trgt; - reg [7:0] counter_next; - reg [7:0] counter_trgt_next; - logic running; - logic running_next; - logic [1:0] s_spi_mode; - - logic sclk_inv; - logic sclk_test; - - assign sdo0 = (en_quad_in) ? data_int[28] : 1'b0; - assign sdo1 = (en_quad_in) ? data_int[29] : data_int[31]; - assign sdo2 = (en_quad_in) ? data_int[30] : 1'b0; - assign sdo3 = (en_quad_in) ? data_int[31] : 1'b0; - - always_comb - begin - done = 1'b0; - if (counter_in_upd) - counter_trgt_next = counter_in; - else - counter_trgt_next = counter_trgt; - - if (counter_in_upd) - running_next = 1'b1; - else if (counter == counter_trgt) - running_next = 1'b0; - else - running_next = running; - - if (running || counter_in_upd) - begin - if (counter == counter_trgt) - begin - done = 1'b1; - counter_next = 0; - end - else - counter_next = counter + 1; - - if (data_valid) - data_int_next = data; - else - begin - if (en_quad_in) - data_int_next = {data_int[27:0],4'b0000}; - else - data_int_next = {data_int[30:0],1'b0}; - end - end - else - begin - counter_next = counter; - data_int_next = data_int; - end - end - - pulp_clock_inverter clk_inv_i - ( - .clk_i(sclk), - .clk_o(sclk_inv) - ); - - pulp_clock_mux2 clk_mux_i - ( - .clk0_i(sclk_inv), - .clk1_i(sclk), - .clk_sel_i(test_mode), - .clk_o(sclk_test) - ); - - always_comb begin : proc_spi_slv_mode - case(s_spi_mode) - `SPI_QUAD_RX: - begin - spi_oen0_o = 1'b1; - spi_oen1_o = 1'b1; - spi_oen2_o = 1'b1; - spi_oen3_o = 1'b1; - end - `SPI_QUAD_TX: - begin - spi_oen0_o = 1'b0; - spi_oen1_o = 1'b0; - spi_oen2_o = 1'b0; - spi_oen3_o = 1'b0; - end - `SPI_STD_TX: - begin - spi_oen0_o = 1'b1; - spi_oen1_o = 1'b0; - spi_oen2_o = 1'b1; - spi_oen3_o = 1'b1; - end - `SPI_STD_RX: - begin - spi_oen0_o = 1'b1; - spi_oen1_o = 1'b1; - spi_oen2_o = 1'b1; - spi_oen3_o = 1'b1; - end - default: - begin - spi_oen0_o = 1'b1; - spi_oen1_o = 1'b1; - spi_oen2_o = 1'b1; - spi_oen3_o = 1'b1; - end - endcase - end - - always @(posedge sclk_test or posedge cs) - begin - if (cs == 1'b1) - begin - counter <= 'h0; - counter_trgt <= 'h7; - data_int <= 'h0; - running <= 1'b0; - s_spi_mode <= `SPI_STD_RX; - end - else - begin - counter <= counter_next; - counter_trgt <= counter_trgt_next; - data_int <= data_int_next; - running <= running_next; - s_spi_mode <= pad_mode; - end - end -endmodule diff --git a/hw/vendor/pulp_platform_axi_spi_slave/src_files.yml b/hw/vendor/pulp_platform_axi_spi_slave/src_files.yml deleted file mode 100644 index 86ab1c508..000000000 --- a/hw/vendor/pulp_platform_axi_spi_slave/src_files.yml +++ /dev/null @@ -1,13 +0,0 @@ -axi_spi_slave: - files: [ - axi_spi_slave_wrap.sv, - axi_spi_slave.sv, - spi_slave_axi_plug.sv, - spi_slave_cmd_parser.sv, - spi_slave_controller.sv, - spi_slave_dc_fifo.sv, - spi_slave_regs.sv, - spi_slave_rx.sv, - spi_slave_syncro.sv, - spi_slave_tx.sv, - ] diff --git a/hw/vendor/pulp_platform_hyperbus.core b/hw/vendor/pulp_platform_hyperbus.core new file mode 100644 index 000000000..ab6f39d36 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus.core @@ -0,0 +1,53 @@ +CAPI=2: + +name: pulp-platform.org::hyperbus + +# Copyright 2021 OpenHW Group +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +filesets: + files_rtl: + depend: + - pulp-platform.org::axi_heep + - x-heep::packages + files: + - pulp_platform_hyperbus/src/hyperbus_pkg.sv + - pulp_platform_hyperbus/src/hyperbus_axi.sv + - pulp_platform_hyperbus/src/hyperbus_cfg_regs.sv + - pulp_platform_hyperbus/src/hyperbus_clk_gen.sv + - pulp_platform_hyperbus/src/hyperbus_clock_diff_out.sv + - pulp_platform_hyperbus/src/hyperbus_ddr_out.sv + - pulp_platform_hyperbus/src/hyperbus_delay.sv + - pulp_platform_hyperbus/src/hyperbus_phy2r.sv + - pulp_platform_hyperbus/src/hyperbus_phy_if.sv + - pulp_platform_hyperbus/src/hyperbus_phy.sv + - pulp_platform_hyperbus/src/hyperbus.sv + - pulp_platform_hyperbus/src/hyperbus_trx.sv + - pulp_platform_hyperbus/src/hyperbus_w2phy.sv + file_type: systemVerilogSource + + rtl_sim: + files: + - pulp_platform_hyperbus/models/configurable_delay.behav.dummy.sv + file_type: systemVerilogSource + + rtl_sim_verilator: + files: + - pulp_platform_hyperbus/models/configurable_delay.behav.dummy.sv + file_type: systemVerilogSource + + rtl_fpga: + files: + - pulp_platform_hyperbus/models/configurable_delay.fpga.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl + - "target_sim? (!tool_verilator ? (rtl_sim))" + - "target_sim? (tool_verilator ? (rtl_sim_verilator))" + - target_nexys-a7-100t? (rtl_fpga) + - target_pynq-z2? (rtl_fpga) + - target_zcu104? (rtl_fpga) diff --git a/hw/vendor/pulp_platform_axi_spi_slave.lock.hjson b/hw/vendor/pulp_platform_hyperbus.lock.hjson similarity index 70% rename from hw/vendor/pulp_platform_axi_spi_slave.lock.hjson rename to hw/vendor/pulp_platform_hyperbus.lock.hjson index 9d7313b2f..f2ae06d59 100644 --- a/hw/vendor/pulp_platform_axi_spi_slave.lock.hjson +++ b/hw/vendor/pulp_platform_hyperbus.lock.hjson @@ -8,7 +8,7 @@ { upstream: { - url: https://github.com/siracusa-soc/axi_spi_slave.git - rev: 6dd735e65edf2dfe884124a4322809c1b3d96790 + url: https://github.com/pulp-platform/hyperbus.git + rev: d5b0064a0579f1a559f003f94721055d497af51d } } diff --git a/hw/vendor/pulp_platform_hyperbus.vendor.hjson b/hw/vendor/pulp_platform_hyperbus.vendor.hjson new file mode 100644 index 000000000..702d43aca --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus.vendor.hjson @@ -0,0 +1,26 @@ +// Copyright 2022 OpenHW Group +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +{ + name: "pulp_platform_hyperbus", + target_dir: "pulp_platform_hyperbus", + + upstream: { + url: "https://github.com/pulp-platform/hyperbus.git", + rev: "d5b0064a0579f1a559f003f94721055d497af51d", + }, + + exclude_from_upstream: [ + ".gitignore", + "scripts", + "test", + "src/hyperbus_stub.sv", + "src/hyperbus_synth_wrap.sv", + ".gitlab-ci.yml", + "Bender.lock", + "Bender.yml", + "CHANGELOG.md", + "CODEOWNERS", + "CONTRIBUTING.md", + ] +} diff --git a/hw/vendor/pulp_platform_axi_spi_slave/LICENSE b/hw/vendor/pulp_platform_hyperbus/LICENSE similarity index 87% rename from hw/vendor/pulp_platform_axi_spi_slave/LICENSE rename to hw/vendor/pulp_platform_hyperbus/LICENSE index 3a2b87e2d..3931d9e54 100644 --- a/hw/vendor/pulp_platform_axi_spi_slave/LICENSE +++ b/hw/vendor/pulp_platform_hyperbus/LICENSE @@ -15,43 +15,43 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. -“License” shall mean the terms and conditions for use, reproduction, and +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -“Licensor” shall mean the Rights owner or entity authorized by the Rights owner +"Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License. -“Legal Entity” shall mean the union of the acting entity and all other entities +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, “control” means (i) the power, direct or +For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -“You” (or “Your”) shall mean an individual or Legal Entity exercising +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -“Rights” means copyright and any similar right including design right (whether +"Rights" means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). -“Source” form shall mean the preferred form for making modifications, including +"Source" form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. -“Object” form shall mean any form resulting from mechanical transformation or +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask works). -“Work” shall mean the work of authorship, whether in Source form or other +"Work" shall mean the work of authorship, whether in Source form or other Object form, made available under the License, as indicated by a Rights notice that is included in or attached to the work (an example is provided in the Appendix below). -“Derivative Works” shall mean any work, whether in Source or Object form, that +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works @@ -59,20 +59,20 @@ shall not include works that remain separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of, the Work and Derivative Works thereof. -“Contribution” shall mean any design or work of authorship, including the +"Contribution" shall mean any design or work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the Rights owner or by an individual or Legal Entity authorized to submit on behalf of the Rights owner. For the purposes of this -definition, “submitted” means any form of electronic, verbal, or written +definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing -by the Rights owner as “Not a Contribution.” +by the Rights owner as "Not a Contribution." -“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. @@ -112,7 +112,7 @@ Source or Object form, provided that You meet the following conditions: the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - If the Work includes a “NOTICE” text file as part of its distribution, then + If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at @@ -145,7 +145,7 @@ reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the @@ -173,4 +173,4 @@ to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. -END OF TERMS AND CONDITIONS +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/hw/vendor/pulp_platform_hyperbus/Makefile b/hw/vendor/pulp_platform_hyperbus/Makefile new file mode 100644 index 000000000..1e5cd12fa --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/Makefile @@ -0,0 +1,64 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Paul Scheffler + +GIT ?= git +BENDER ?= bender +VSIM ?= vsim + +all: build run + +clean: sim_clean + +# Ensure half-built targets are purged +.DELETE_ON_ERROR: + +ifdef gui +VSIM_ARGS := -do +else +VSIM_ARGS := -c -do +endif + +# -------------- +# RTL SIMULATION +# -------------- + +VLOG_ARGS += -suppress vlog-2583 -suppress vlog-13314 -suppress vlog-13233 -timescale \"1 ns / 1 ps\" +XVLOG_ARGS += -64bit -compile -vtimescale 1ns/1ns -quiet + +define generate_vsim + echo 'set ROOT [file normalize [file dirname [info script]]/$3]' > $1 + bender script $(VSIM) --vlog-arg="$(VLOG_ARGS)" $2 | grep -v "set ROOT" >> $1 + echo >> $1 +endef + +sim_all: scripts/compile.tcl + +sim_clean: + rm -rf scripts/compile.tcl + rm -rf work + +# Download (partially non-free) simulation models from publically available sources; +# by running these targets or targets depending on them, you accept this (see README.md). +models/s27ks0641: + mkdir -p $@ + rm -rf model_tmp && mkdir model_tmp + cd model_tmp; wget https://www.infineon.com/dgdl/Infineon-S27KL0641_S27KS0641_VERILOG-SimulationModels-v05_00-EN.zip?fileId=8ac78c8c7d0d8da4017d0f6349a14f68 + cd model_tmp; mv 'Infineon-S27KL0641_S27KS0641_VERILOG-SimulationModels-v05_00-EN.zip?fileId=8ac78c8c7d0d8da4017d0f6349a14f68' model.zip + cd model_tmp; unzip model.zip + cd model_tmp; mv 'S27KL0641 S27KS0641' exe_folder + cd model_tmp/exe_folder; unzip S27ks0641.exe + cp model_tmp/exe_folder/S27ks0641/model/s27ks0641.v $@ + cp model_tmp/exe_folder/S27ks0641/model/s27ks0641_verilog.sdf models/s27ks0641/s27ks0641.sdf + rm -rf model_tmp + +scripts/compile.tcl: Bender.yml models/s27ks0641 + $(call generate_vsim, $@, -t rtl -t test -t hyper_test,..) + +build: scripts/compile.tcl + $(VSIM) -c -do "source scripts/compile.tcl; exit" + +run: clean build + $(VSIM) $(VSIM_ARGS) "source scripts/start.tcl" diff --git a/hw/vendor/pulp_platform_hyperbus/README.md b/hw/vendor/pulp_platform_hyperbus/README.md new file mode 100644 index 000000000..57a88946a --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/README.md @@ -0,0 +1,49 @@ +# HyperBus v2 + +This peripheral implements an AXI4-compliant interface for the HyperBus protocol, described [in its specification](https://www.cypress.com/file/213356/download). HyperBus v2 is part of the [PULP (Parallel Ultra-Low-Power) platform](https://pulp-platform.org/). + +Hyperbus is mainly used for off-chip memories (HyperFlash and HyperRAM), but also supported by some generic peripherals. + +## Features + +The AXI data widths are parameterizable from 16 to 1024, with transfers of all sizes possible. It is also possible to decide how many memories to connect on the same HyperBUS to set the overall available memory. + +Multiple memories on the same bus are placed contiguously in the address memory map and selected through their dedicated CS. At runtime, one can communicate to the controller the size +of the HyperRAMs, and the controller will demultiplex the transactions accordingly. Also, one can choose how many HyperBUS interfaces (1 or 2) to expose. Both buses will have the same number of CSs. +When exposing 2 HyperBUSes, the pair of memories on the same chip select will be mapped as interleaved: each memory will be seen as a memory block of 16-bit width. Doing so will double the maximum achievable bandwidth, up to 6.4 Gbps, +doubling the pin count. + +The main restrictions are as follows: + +- Atomics are not supported. +- Only linear bursts are supported. +- All accesses except byte-size accesses must be aligned to 16-bit boundaries. +- Only communication with HyperRAMs has been tested. Support for flash is WIP. + +The address width is also fully parameterizable. We support bursts of any size permitted by AXI and stalling through PHY-level clock stopping and protocol-level burst splitting. We do _not_ buffer bursts to support devices without the clock stop feature; please ensure your device supports clock stop or ensure sufficient buffering upstream. + +The configuration register interface uses the minimal Regbus protocol. Data and address widths are parameterizable, but register sizes must be a power of two larger than 16 bits. + +## Architecture + +The block diagram below outlines the approximate architecture. Note that there are *two* internally provided clock domains,`clk_sys` and `clk_phy`, as well as an incoming clock `clk_rwds_in` which is then internally delayed. +The `clk_phy` and `clk_phy_90` have a 90 degree difference in phase. To obtain the two clocks one can either shift the input `clk_phy_i` with the clock delayer or to use the `ddr_clk` module that halves the frequency and generated 4 90-degree shifted clocks. + +![HyperBus v2 block diagram](./docs/axi_hyper.svg) + +## Simulation + +† * `models/s27ks0641/s27ks0641.v` will download externally provided peripheral simulation models, some proprietary and with non-free license terms, from their publically accessible sources; see `Makefile` for details. By running `models/s27ks0641/s27ks0641.v` or the default target `run`, you accept this.* + +To run a simulation you need [Bender](https://github.com/pulp-platform/bender) and Questasim. Export your path to include your bender binary, and then: + +```bash +bender update +make run #(will download proprietary models from Infineon !!) +``` + +## ToDos + +- [ ] Support byte-aligned accesses for non-byte-size transfer +- [ ] Test HyperFlash +- [ ] PSRAM support through additional CA decoder diff --git a/hw/vendor/pulp_platform_hyperbus/VERSION b/hw/vendor/pulp_platform_hyperbus/VERSION new file mode 100644 index 000000000..8acdd82b7 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/VERSION @@ -0,0 +1 @@ +0.0.1 diff --git a/hw/vendor/pulp_platform_hyperbus/docs/axi_hyper.svg b/hw/vendor/pulp_platform_hyperbus/docs/axi_hyper.svg new file mode 100644 index 000000000..34fe96ace --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/docs/axi_hyper.svg @@ -0,0 +1,4 @@ + + + +
AR/AW
AR/AW
W
W
B
B
R
R
Phy clk
Phy clk
APB
APB
H
Y
P
E
R
B
U
S
1
H...



Ax queue
Ax queue...
FSM
FSM
H
Y
P
E
R
B
U
S
0
H...
TX
TX
RX
RX
PHY R
TO 
AXI R
PHY R...
Delay
lines
Delay...
DIV &
SHIFT
DIV &...
AXI W
TO
PHY W
AXI W...
AX
TO
PHY
AX...
CFG
REGS
CFG...
MEM
1
MEM...
MEM
2
MEM...
MEM
3
MEM...
MEM
0
MEM...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/hw/vendor/pulp_platform_hyperbus/docs/wavedrom_input.png b/hw/vendor/pulp_platform_hyperbus/docs/wavedrom_input.png new file mode 100644 index 000000000..6b57c48be Binary files /dev/null and b/hw/vendor/pulp_platform_hyperbus/docs/wavedrom_input.png differ diff --git a/hw/vendor/pulp_platform_hyperbus/docs/wavedrom_input.txt b/hw/vendor/pulp_platform_hyperbus/docs/wavedrom_input.txt new file mode 100644 index 000000000..a51f23aec --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/docs/wavedrom_input.txt @@ -0,0 +1,14 @@ +{signal: [ + {name: 'rwds_i', wave: '0p..' , period:4 , node:'....'}, + {name: 'rwds_delayed', wave: '0....1.0.1.0.1.0', node:'.....B...'}, + {name: 'reg_d=dq_i', wave: 'z...3.4.5.6.x...', data: ['A0', 'A1', 'B0', 'B1'], node:'....A.H..' }, + {name: 'fifo_in[15:8]=reg_q', wave: 'z.....3...5...x..', data: ['A0', 'B0'] , phase: 0.5, node:'......C......'}, + {name: 'rwds_delayed_n', wave: '1....0.1.0.1.0.1', node:'.......s..'}, + {name: 'fifo_in[7:0]=dq_i', wave: 'z.3456x.', period:2, data: ['A0', 'A1', 'B0', 'B1'], node:'...EO' } , + +], + edge: [ +'A|->B ts', 'b|g','B~C ', 'C|->s ts', 'E|->s ts', 'B-|H th', 's-|O th' +] + +} diff --git a/hw/vendor/pulp_platform_hyperbus/hyperbus_regs.hjson b/hw/vendor/pulp_platform_hyperbus/hyperbus_regs.hjson new file mode 100644 index 000000000..f1fa4c3be --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/hyperbus_regs.hjson @@ -0,0 +1,92 @@ +// Copyright EPFL contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// HyperBus Register Description + +{ name: "hyperbus", + clock_primary: "clk_i", + bus_interfaces: [ + { protocol: "reg_iface", direction: "device" } + ], + regwidth: "32", + registers: [ + + { name: "T_LATENCY_ACCESS" + desc: "T_LATENCY_ACCESS register" + swaccess: "rw" + fields: [ + { bits: "3:0", name: "T_LATENCY_ACCESS", desc: "T_LATENCY_ACCESS register" } + ] + } + { name: "EN_LATENCY_ADDITIONAL" + desc: "EN_LATENCY_ADDITIONAL register" + swaccess: "rw" + fields: [ + { bits: "0", name: "EN_LATENCY_ADDITIONAL", desc: "EN_LATENCY_ADDITIONAL register" } + ] + } + { name: "T_BURST_MAX" + desc: "T_BURST_MAX register" + swaccess: "rw" + fields: [ + { bits: "15:0", name: "T_BURST_MAX", desc: "T_BURST_MAX register" } + ] + } + { name: "T_READ_WRITE_RECOVERY" + desc: "T_READ_WRITE_RECOVERY register" + swaccess: "rw" + fields: [ + { bits: "3:0", name: "T_READ_WRITE_RECOVERY", desc: "T_READ_WRITE_RECOVERY register" } + ] + } + { name: "T_RX_CLK_DELAY" + desc: "T_RX_CLK_DELAY register" + swaccess: "rw" + fields: [ + { bits: "3:0", name: "T_RX_CLK_DELAY", desc: "T_RX_CLK_DELAY register" } + ] + } + { name: "T_TX_CLK_DELAY" + desc: "T_TX_CLK_DELAY register" + swaccess: "rw" + fields: [ + { bits: "3:0", name: "T_TX_CLK_DELAY", desc: "T_TX_CLK_DELAY register" } + ] + } + { name: "ADDRESS_MASK_MSB" + desc: "ADDRESS_MASK_MSB register" + swaccess: "rw" + fields: [ + { bits: "4:0", name: "ADDRESS_MASK_MSB", desc: "ADDRESS_MASK_MSB register" } + ] + } + { name: "ADDRESS_SPACE" + desc: "ADDRESS_SPACE register" + swaccess: "rw" + fields: [ + { bits: "0", name: "ADDRESS_SPACE", desc: "ADDRESS_SPACE register" } + ] + } + { name: "PHYS_IN_USE" + desc: "PHYS_IN_USE register" + swaccess: "rw" + fields: [ + { bits: "0", name: "PHYS_IN_USE", desc: "PHYS_IN_USE register" } + ] + } + { name: "WHICH_PHY" + desc: "WHICH_PHY register" + swaccess: "rw" + fields: [ + { bits: "0", name: "WHICH_PHY", desc: "WHICH_PHY register" } + ] + } + { name: "T_CSH_CYCLE" + desc: "T_CSH_CYCLE register" + swaccess: "rw" + fields: [ + { bits: "3:0", name: "T_CSH_CYCLE", desc: "T_CSH_CYCLE register" } + ] + } + ] +} diff --git a/hw/vendor/pulp_platform_hyperbus/models/README.md b/hw/vendor/pulp_platform_hyperbus/models/README.md new file mode 100644 index 000000000..d9ee9adc0 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/models/README.md @@ -0,0 +1,17 @@ +# Simulation Models + +This folder is just a placeholder. The verilog model of the Spansion s27ks0641 ram is proprietary code of CypressSemiconductor Corporation, which can currently be downloaded from [here](https://www.infineon.com/dgdl/Infineon-S27KL0641_S27KS0641_VERILOG-SimulationModels-v05_00-EN.zip?fileId=8ac78c8c7d0d8da4017d0f6349a14f68) + +Once the package has been downloaded, the following steps are necessary to integrate the model in the platform: + +1. unzip it (it might be necessary to rename it 260016.zip first) +2. execute s27ks0641.exe +3. move the newly created `s27ks0641.v` file in `models/s27ks0641/s27ks0641.v`, according to the `Bender.yml` + +## Vendor Specific Peripherals + +- `s27ks0641`: Cypress, HyperBus pseudo SRAM model/self-refresh DRAM (8/16 MByte) + +## Generic Delay + +- `generic_delay_D4_O1_3P750_CG0.behav.sv`: Delay macro with 4-bit delay control, 1 output, 3.75 ns total delay, no clock gate diff --git a/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.behav.dummy.sv b/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.behav.dummy.sv new file mode 100644 index 000000000..2bfc4d8c9 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.behav.dummy.sv @@ -0,0 +1,27 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Davide Schiavone + +// Dummy Empy Module just to let Verilator Compile + +module configurable_delay #( + parameter int unsigned NUM_STEPS, // The desired number of delay taps. Must be + // a power of 2. Don't use very large values + // here, otherwise strategy to just let STA + // (with the right SDC) do the job for us + // will not work. + localparam DELAY_SEL_WIDTH = $clog2(NUM_STEPS) +) ( + input logic clk_i, + /* verilator lint_off UNUSED */ + input logic enable_i, + /* verilator lint_off UNUSED */ + input logic [DELAY_SEL_WIDTH-1:0] delay_i, + output logic clk_o +); + assign clk_o = clk_i; + + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.behav.sv b/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.behav.sv new file mode 100644 index 000000000..f000ede59 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.behav.sv @@ -0,0 +1,39 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Thomas Benz +// Paul Scheffler +// +// Based on work of: +// Fabian Schuiki +// Florian Zaruba + + +// Automatically generated by the Generic Delay generator. +`timescale 1ps/1ps + +(* no_ungroup *) +(* no_boundary_optimization *) +module configurable_delay #( + parameter int unsigned NUM_STEPS, // The desired number of delay taps. Must be + // a power of 2. Don't use very large values + // here, otherwise strategy to just let STA + // (with the right SDC) do the job for us + // will not work. + localparam DELAY_SEL_WIDTH = $clog2(NUM_STEPS) +) ( + input logic clk_i, + input logic enable_i, + input logic [DELAY_SEL_WIDTH-1:0] delay_i, + output logic clk_o +); + + logic enable_latched; + logic clk; + + assign clk = clk_i; + + always @(clk) clk_o <= #(real'(delay_i)*3.750ns/15) clk; + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.fpga.sv b/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.fpga.sv new file mode 100644 index 000000000..11be16c92 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/models/configurable_delay.fpga.sv @@ -0,0 +1,39 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Thomas Benz +// Paul Scheffler +// +// Based on work of: +// Fabian Schuiki +// Florian Zaruba + +`timescale 1ps/1ps + +(* no_ungroup *) +(* no_boundary_optimization *) +module configurable_delay #( + parameter int unsigned NUM_STEPS, // The desired number of delay taps. Must be + // a power of 2. Don't use very large values + // here, otherwise strategy to just let STA + // (with the right SDC) do the job for us + // will not work. + localparam DELAY_SEL_WIDTH = $clog2(NUM_STEPS) +) ( + input logic clk_i, + input logic enable_i, + input logic [DELAY_SEL_WIDTH-1:0] delay_i, + output logic clk_o +); + + IBUF # + ( + .IBUF_LOW_PWR ("FALSE") + ) u_ibufg_sys_clk_o + ( + .I (clk_i), + .O (clk_o) + ); + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus.sv new file mode 100644 index 000000000..555d35c19 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus.sv @@ -0,0 +1,301 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Thomas Benz +// Paul Scheffler +// Luca Valente + +module hyperbus #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter int unsigned IsClockODelayed = 0, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type axi_w_chan_t = logic, + parameter type axi_b_chan_t = logic, + parameter type axi_ar_chan_t = logic, + parameter type axi_r_chan_t = logic, + parameter type axi_aw_chan_t = logic, + parameter int unsigned RegDataWidth = -1, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + // The below have sensible defaults, but should be set on integration! + parameter int unsigned RxFifoLogDepth = 2, + parameter int unsigned TxFifoLogDepth = 2, + parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, // Base address for all chips + parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, // 64 KiB: Current maximum HyperBus device size + parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys), + parameter int unsigned PhyStartupCycles = 300 * 200, /* us*MHz */ // Conservative maximum frequency estimate + parameter int unsigned SyncStages = 2 +) ( + input logic clk_phy_i, + input logic rst_phy_ni, + input logic clk_sys_i, + input logic rst_sys_ni, + input logic test_mode_i, + // AXI bus + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + // Reg bus + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // Physical interace: facing HyperBus PADs + output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0] hyper_ck_o, + output logic [NumPhys-1:0] hyper_ck_no, + output logic [NumPhys-1:0] hyper_rwds_o, + input logic [NumPhys-1:0] hyper_rwds_i, + output logic [NumPhys-1:0] hyper_rwds_oe_o, + input logic [NumPhys-1:0][7:0] hyper_dq_i, + output logic [NumPhys-1:0][7:0] hyper_dq_o, + output logic [NumPhys-1:0] hyper_dq_oe_o, + output logic [NumPhys-1:0] hyper_reset_no +); + + + // Combined transfer type for CDC + typedef struct packed { + hyperbus_pkg::hyper_tf_t trans; + logic [NumChips-1:0] cs; + } tf_cdc_t; + + + logic clk_phy_i_0, clk_phy_i_90, rst_phy; + + // Register file + hyperbus_pkg::hyper_cfg_t cfg; + axi_rule_t [NumChips-1:0] chip_rules; + logic trans_active; + + // AXI slave + hyperbus_pkg::hyper_rx_t axi_rx; + logic axi_rx_valid; + logic axi_rx_ready; + hyperbus_pkg::hyper_tx_t axi_tx; + logic axi_tx_valid; + logic axi_tx_ready; + logic axi_b_error; + logic axi_b_valid; + logic axi_b_ready; + tf_cdc_t axi_tf_cdc; + logic axi_trans_valid; + logic axi_trans_ready; + + // PHY + hyperbus_pkg::hyper_rx_t phy_rx; + logic phy_rx_valid; + logic phy_rx_ready; + hyperbus_pkg::hyper_tx_t phy_tx; + logic phy_tx_valid; + logic phy_tx_ready; + logic phy_b_error; + logic phy_b_valid; + logic phy_b_ready; + tf_cdc_t phy_tf_cdc; + logic phy_trans_valid; + logic phy_trans_ready; + + // Config register File + hyperbus_cfg_regs #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .RegDataWidth ( RegDataWidth ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .rule_t ( axi_rule_t ), + .RstChipBase ( RstChipBase ), + .RstChipSpace ( RstChipSpace ), + .RstCfg ( RstCfg ) + ) i_cfg_regs ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .reg_req_i ( reg_req_i ), + .reg_rsp_o ( reg_rsp_o ), + .cfg_o ( cfg ), + .chip_rules_o ( chip_rules ), + .trans_active_i ( trans_active ) + ); + + // AXI slave interfacing PHY + hyperbus_axi #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .hyper_rx_t ( hyperbus_pkg::hyper_rx_t ), + .hyper_tx_t ( hyperbus_pkg::hyper_tx_t ), + .rule_t ( axi_rule_t ) + ) i_axi_slave ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + + .rx_i ( axi_rx ), + .rx_valid_i ( axi_rx_valid ), + .rx_ready_o ( axi_rx_ready ), + .tx_o ( axi_tx ), + .tx_valid_o ( axi_tx_valid ), + .tx_ready_i ( axi_tx_ready ), + .b_error_i ( axi_b_error ), + .b_valid_i ( axi_b_valid ), + .b_ready_o ( axi_b_ready ), + .trans_o ( axi_tf_cdc.trans ), + .trans_cs_o ( axi_tf_cdc.cs ), + .trans_valid_o ( axi_trans_valid ), + .trans_ready_i ( axi_trans_ready ), + + .chip_rules_i ( chip_rules ), + .which_phy_i ( cfg.which_phy ), + .phys_in_use_i ( cfg.phys_in_use ), + .addr_mask_msb_i ( cfg.address_mask_msb ), + .addr_space_i ( cfg.address_space ), + .trans_active_o ( trans_active ) + ); + + hyperbus_phy_if #( + .NumChips ( NumChips ), + .StartupCycles ( PhyStartupCycles ), + .NumPhys ( NumPhys ), + .hyper_rx_t ( hyperbus_pkg::hyper_rx_t ), + .hyper_tx_t ( hyperbus_pkg::hyper_tx_t ), + .SyncStages ( SyncStages ) + ) i_phy ( + .clk_i ( clk_phy_i_0 ), + .clk_i_90 ( clk_phy_i_90 ), + .rst_ni ( rst_phy ), + .test_mode_i ( test_mode_i ), + + .cfg_i ( cfg ), + + .rx_o ( phy_rx ), + .rx_valid_o ( phy_rx_valid ), + .rx_ready_i ( phy_rx_ready ), + .tx_i ( phy_tx ), + .tx_valid_i ( phy_tx_valid ), + .tx_ready_o ( phy_tx_ready ), + .b_error_o ( phy_b_error ), + .b_valid_o ( phy_b_valid ), + .b_ready_i ( phy_b_ready ), + .trans_i ( phy_tf_cdc.trans ), + .trans_cs_i ( phy_tf_cdc.cs ), + .trans_valid_i ( phy_trans_valid ), + .trans_ready_o ( phy_trans_ready ), + + .hyper_cs_no ( hyper_cs_no ), + .hyper_ck_o ( hyper_ck_o ), + .hyper_ck_no ( hyper_ck_no ), + .hyper_rwds_o ( hyper_rwds_o ), + .hyper_rwds_i ( hyper_rwds_i ), + .hyper_rwds_oe_o( hyper_rwds_oe_o ), + .hyper_dq_i ( hyper_dq_i ), + .hyper_dq_o ( hyper_dq_o ), + .hyper_dq_oe_o ( hyper_dq_oe_o ), + .hyper_reset_no ( hyper_reset_no ) + ); + + cdc_2phase #( + .T ( tf_cdc_t ) + ) i_cdc_2phase_trans ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_data_i ( axi_tf_cdc ), + .src_valid_i ( axi_trans_valid ), + .src_ready_o ( axi_trans_ready ), + + .dst_rst_ni ( rst_phy ), + .dst_clk_i ( clk_phy_i_0 ), + .dst_data_o ( phy_tf_cdc ), + .dst_valid_o ( phy_trans_valid ), + .dst_ready_i ( phy_trans_ready ) + ); + + cdc_2phase #( + .T ( logic ) + ) i_cdc_2phase_b ( + .src_rst_ni ( rst_phy ), + .src_clk_i ( clk_phy_i_0 ), + .src_data_i ( phy_b_error ), + .src_valid_i ( phy_b_valid ), + .src_ready_o ( phy_b_ready ), + + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_data_o ( axi_b_error ), + .dst_valid_o ( axi_b_valid ), + .dst_ready_i ( axi_b_ready ) + ); + + // Write data, TX CDC FIFO + cdc_fifo_gray #( + .T ( hyperbus_pkg::hyper_tx_t ), + .LOG_DEPTH ( TxFifoLogDepth ) + ) i_cdc_fifo_tx ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_data_i ( axi_tx ), + .src_valid_i ( axi_tx_valid ), + .src_ready_o ( axi_tx_ready ), + + .dst_rst_ni ( rst_phy ), + .dst_clk_i ( clk_phy_i_0 ), + .dst_data_o ( phy_tx ), + .dst_valid_o ( phy_tx_valid ), + .dst_ready_i ( phy_tx_ready ) + ); + + // Read data, RX CDC FIFO + cdc_fifo_gray #( + .T ( hyperbus_pkg::hyper_rx_t ), + .LOG_DEPTH ( RxFifoLogDepth ) + ) i_cdc_fifo_rx ( + .src_rst_ni ( rst_phy ), + .src_clk_i ( clk_phy_i_0 ), + .src_data_i ( phy_rx ), + .src_valid_i ( phy_rx_valid ), + .src_ready_o ( phy_rx_ready ), + + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_data_o ( axi_rx ), + .dst_valid_o ( axi_rx_valid ), + .dst_ready_i ( axi_rx_ready ) + ); + + // Shift clock by 90 degrees + generate + if(IsClockODelayed==0) begin : clock_generator + hyperbus_clk_gen ddr_clk ( + .clk_i ( clk_phy_i ), + .rst_ni ( rst_phy_ni ), + .clk0_o ( clk_phy_i_0 ), + .clk90_o ( clk_phy_i_90 ), + .clk180_o ( ), + .clk270_o ( ), + .rst_no ( rst_phy ) + ); + end else if (IsClockODelayed==1) begin + assign clk_phy_i_0 = clk_phy_i; + assign rst_phy = rst_phy_ni; + hyperbus_delay i_delay_tx_clk_90 ( + .in_i ( clk_phy_i_0 ), + .delay_i ( cfg.t_tx_clk_delay ), + .out_o ( clk_phy_i_90 ) + ); + end + endgenerate + + + +endmodule : hyperbus diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_axi.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_axi.sv new file mode 100644 index 000000000..612175935 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_axi.sv @@ -0,0 +1,545 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Thomas Benz +// Paul Scheffler + +`include "axi/typedef.svh" +`include "common_cells/registers.svh" + +module hyperbus_axi #( + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = -1, + parameter type hyper_tx_t = logic, + parameter type hyper_rx_t = logic, + parameter type rule_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // AXI port + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + // PHI port + input hyper_rx_t rx_i, + input logic rx_valid_i, + output logic rx_ready_o, + + output hyper_tx_t tx_o, + output logic tx_valid_o, + input logic tx_ready_i, + + input logic b_error_i, + input logic b_valid_i, + output logic b_ready_o, + + output hyperbus_pkg::hyper_tf_t trans_o, + output logic [NumChips-1:0] trans_cs_o, + output logic trans_valid_o, + input logic trans_ready_i, + + input rule_t [NumChips-1:0] chip_rules_i, + input logic phys_in_use_i, + input logic which_phy_i, + input logic [4:0] addr_mask_msb_i, + input logic addr_space_i, + output logic trans_active_o +); + + localparam AxiDataBytes = AxiDataWidth/8; + localparam AxiBusAddrWidth = $clog2(AxiDataBytes); + localparam PhyDataWidth = NumPhys*16; + localparam PhyDataBytes = PhyDataWidth/8; + localparam ChipSelWidth = cf_math_pkg::idx_width(NumChips); + localparam ByteCntWidth = cf_math_pkg::idx_width(AxiDataBytes); + + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + typedef logic [ByteCntWidth-1:0] byte_cnt_t; + typedef logic [ByteCntWidth-3:0] word_cnt_t; + typedef logic [AxiDataWidth-1:0] axi_data_t; + typedef logic [ChipSelWidth-1:0] chip_sel_idx_t; + + `AXI_TYPEDEF_ALL_CT(axi_fifo,axi_fifo_req,axi_fifo_rsp,axi_addr_t,logic[AxiIdWidth-1:0],axi_data_t,logic[AxiDataBytes-1:0],logic[AxiUserWidth-1:0]) + + // No need to track ID: serializer buffers it for us + typedef struct packed { + axi_addr_t addr; + axi_pkg::len_t len; + axi_pkg::burst_t burst; + axi_pkg::size_t size; + } axi_ax_t; + + typedef struct packed { + axi_ax_t ax_data; + logic write; + } ax_channel_spill_t; + + typedef struct packed { + logic valid; + axi_data_t data; + logic error; + logic last; + } axi_r_t; + + typedef struct packed { + logic [AxiDataWidth/8-1:0] strb; + axi_data_t data; + logic [AxiUserWidth-1:0] user; + logic last; + } axi_w_chan_t; + + typedef struct packed { + logic [7:0] data; + logic strb; + } axi_wbyte_t; + + // AXI FIFO downstream + axi_req_t fifo_out_req; + axi_rsp_t fifo_out_rsp; + + // Atomics Filter downstream + axi_req_t atop_out_req; + axi_rsp_t atop_out_rsp; + + // ID serializer downstream + axi_req_t ser_out_req; + axi_rsp_t ser_out_rsp; + axi_ax_t ser_out_req_aw; + axi_ax_t ser_out_req_ar; + + // AX arbiter downstream + axi_ax_t rr_out_req_ax; + logic rr_out_req_write; + logic spill_ax_valid, spill_ax_ready; + axi_ax_t spill_rr_out_req_ax; + logic spill_rr_out_req_write; + ax_channel_spill_t spill_ax_channel_in, spill_ax_channel_out; + + // AX handling + logic trans_handshake; + logic ax_valid, ax_ready; + chip_sel_idx_t ax_chip_sel_idx; + hyperbus_pkg::hyper_blen_t ax_blen_postinc; + logic ax_blen_inc; + + // R channel + axi_r_t s_r_split; + + // R channel merge when phys_in_use != NumPhys + logic [PhyDataWidth-1:0] s_rx_data; + logic [PhyDataWidth/2-1:0] s_rx_data_lower_d, s_rx_data_lower_q; + logic s_rx_error; + logic s_rx_last; + logic s_rx_valid; + logic s_rx_ready; + logic merge_r_d, merge_r_q; + + // W channel + logic w_data_valid; + logic w_data_ready; + axi_w_chan_t w_data_fifo; + axi_w_chan_t w_data_fifo_in; + + // W channel split when phys_in_use != NumPhys + logic [PhyDataWidth-1:0] s_tx_data; + logic [PhyDataBytes-1:0] s_tx_strb; + logic s_tx_last; + logic s_tx_valid; + logic s_tx_ready; + logic split_w_d, split_w_q; + + // Whether a transfer is currently active + logic trans_active_d, trans_active_q; + logic trans_active_set, trans_active_reset; + logic trans_wready_d, trans_wready_q; + logic trans_wready_set, trans_wready_reset; + + // ============================ + // Serialize requests + // ============================ + + axi_fifo #( + .Depth ( 4 ), + .FallThrough ( 1'b0 ), + .aw_chan_t ( axi_fifo_aw_chan_t ), + .w_chan_t ( axi_fifo_w_chan_t ), + .b_chan_t ( axi_fifo_b_chan_t ), + .ar_chan_t ( axi_fifo_ar_chan_t ), + .r_chan_t ( axi_fifo_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_rsp_t ) + ) i_axi_fifo ( + .clk_i, + .rst_ni, + .test_i ( 1'b0 ), + .slv_req_i ( axi_req_i ), + .slv_resp_o ( axi_rsp_o ), + .mst_req_o ( fifo_out_req ), + .mst_resp_i ( fifo_out_rsp ) + ); + + + // Block unsupported atomics + axi_atop_filter #( + .AxiIdWidth ( AxiIdWidth ), + .AxiMaxWriteTxns ( 1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_rsp_t ) + ) i_axi_atop_filter ( + .clk_i, + .rst_ni, + .slv_req_i ( fifo_out_req ), + .slv_resp_o ( fifo_out_rsp ), + .mst_req_o ( atop_out_req ), + .mst_resp_i ( atop_out_rsp ) + ); + + // Ensure we only handle one ID (master) at a time + axi_serializer #( + .MaxReadTxns ( 1 ), + .MaxWriteTxns ( 1 ), + .AxiIdWidth ( AxiIdWidth ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_rsp_t ) + ) i_axi_serializer ( + .clk_i, + .rst_ni, + .slv_req_i ( atop_out_req ), + .slv_resp_o ( atop_out_rsp ), + .mst_req_o ( ser_out_req ), + .mst_resp_i ( ser_out_rsp ) + ); + + // Round-robin-arbitrate between AR and AW channels (HyperBus is simplex) + assign ser_out_req_ar.addr = ser_out_req.ar.addr; + assign ser_out_req_ar.len = ser_out_req.ar.len; + assign ser_out_req_ar.burst = ser_out_req.ar.burst; + assign ser_out_req_ar.size = ser_out_req.ar.size; + + assign ser_out_req_aw.addr = ser_out_req.aw.addr; + assign ser_out_req_aw.len = ser_out_req.aw.len; + assign ser_out_req_aw.burst = ser_out_req.aw.burst; + assign ser_out_req_aw.size = ser_out_req.aw.size; + + rr_arb_tree #( + .NumIn ( 2 ), + .DataType ( axi_ax_t ), + .AxiVldRdy ( 1 ), + .ExtPrio ( 1'b1 ) + ) i_rr_arb_tree_ax ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .req_i ( { ser_out_req.aw_valid, ser_out_req.ar_valid } ), + .gnt_o ( { ser_out_rsp.aw_ready, ser_out_rsp.ar_ready } ), + .data_i ( { ser_out_req_aw, ser_out_req_ar } ), + .req_o ( spill_ax_valid ), + .gnt_i ( spill_ax_ready ), + .data_o ( spill_rr_out_req_ax ), + .idx_o ( spill_rr_out_req_write ) + ); + + // Cut paths between serializer and rr arb tree + assign spill_ax_channel_in.ax_data = spill_rr_out_req_ax; + assign spill_ax_channel_in.write = spill_rr_out_req_write; + + spill_register #( + .T ( ax_channel_spill_t ) + ) ax_spill_register ( + .clk_i, + .rst_ni, + .valid_i (spill_ax_valid), + .ready_o (spill_ax_ready), + .data_i (spill_ax_channel_in), + .valid_o (ax_valid), + .ready_i (ax_ready), + .data_o (spill_ax_channel_out) + ); + + assign rr_out_req_ax = spill_ax_channel_out.ax_data; + assign rr_out_req_write = spill_ax_channel_out.write; + + assign trans_valid_o = ax_valid & ~trans_active_q; + assign ax_ready = trans_ready_i & ~trans_active_q; + + assign trans_handshake = trans_valid_o & trans_ready_i; + + // ============================ + // AX channel: handle + // ============================ + + // Handle address mapping to chip select + addr_decode #( + .NoIndices ( NumChips ), + .NoRules ( NumChips ), + .addr_t ( axi_addr_t ), + .rule_t ( rule_t ) + ) i_addr_decode_chip_sel ( + .addr_i ( rr_out_req_ax.addr ), + .addr_map_i ( chip_rules_i ), + .idx_o ( ax_chip_sel_idx ), + .dec_valid_o ( ), + .dec_error_o ( ), + .en_default_idx_i ( 1'b1 ), + .default_idx_i ( '0 ) + ); + + // Chip select binary to one hot decoding + always_comb begin : proc_comb_trans_cs + trans_cs_o = '0; + trans_cs_o[ax_chip_sel_idx] = 1'b1; + end + + // AX channel: forward, converting unmasked byte to masked word addresses + assign trans_o.write = rr_out_req_write; + assign trans_o.burst_type = 1'b1; // Wrapping bursts not (yet) supported + assign trans_o.address_space = addr_space_i; + assign trans_o.address = ( (rr_out_req_ax.addr & ~32'(32'hFFFF_FFFF << addr_mask_msb_i)) >> ( NumPhys ) ) << ( (NumPhys==2) & ~phys_in_use_i ); + + // Convert burst length from decremented, unaligned beats to non-decremented, aligned 16-bit words + always_comb begin + trans_o.burst= NumPhys; + ax_blen_inc = 1'b1; + if (rr_out_req_ax.size > NumPhys) begin + ax_blen_inc = 1'b1; + if( ((rr_out_req_ax.addr>>rr_out_req_ax.size)<>rr_out_req_ax.size)<> NumPhys ) + 1 ) << (NumPhys-1); + end else begin + trans_o.burst = ( ( ( rr_out_req_ax.addr[NumPhys-1:0] + (ax_blen_postinc<> NumPhys ) + 1 ) << (NumPhys-1); + end + end + end + end + + assign ax_blen_postinc = rr_out_req_ax.len + hyperbus_pkg::hyper_blen_t'(ax_blen_inc) ; + + // ============================ + // R channel + // ============================ + + assign ser_out_rsp.r.data = s_r_split.data; + assign ser_out_rsp.r.last = s_r_split.last; + assign ser_out_rsp.r.resp = s_r_split.error ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY; + assign ser_out_rsp.r.id = '0; + assign ser_out_rsp.r.user = '0; + + always_comb begin + s_rx_valid = rx_valid_i; + rx_ready_o = s_rx_ready; + s_rx_data = rx_i.data; + s_rx_last = rx_i.last; + s_rx_error = rx_i.error; + s_rx_data_lower_d = s_rx_data_lower_q; + merge_r_d = merge_r_q; + if( (NumPhys==2) & (~phys_in_use_i) ) begin + if(rx_valid_i & s_rx_ready) begin + merge_r_d = merge_r_q + 1; + end + if(~which_phy_i) + s_rx_data = { rx_i.data[PhyDataWidth/2-1:0] , s_rx_data_lower_q }; + else + s_rx_data = { rx_i.data[PhyDataWidth-1:PhyDataWidth/2] , s_rx_data_lower_q }; + s_rx_valid = rx_valid_i & merge_r_q; + rx_ready_o = s_rx_ready; + s_rx_last = rx_i.last & merge_r_q; + s_rx_error = rx_i.error; + if(~merge_r_q) begin + if(~which_phy_i) + s_rx_data_lower_d = rx_i.data[PhyDataWidth/2-1:0]; + else + s_rx_data_lower_d = rx_i.data[PhyDataWidth-1:PhyDataWidth/2]; + end + end + end + + hyperbus_phy2r #( + .AxiDataWidth ( AxiDataWidth ), + .BurstLength ( hyperbus_pkg::HyperBurstWidth ), + .T ( axi_r_t ), + .NumPhys ( NumPhys ) + ) i_hyperbus_phy2r ( + .clk_i, + .rst_ni, + .size ( rr_out_req_ax.size ), + .trans_handshake ( trans_handshake ), + .start_addr ( rr_out_req_ax.addr[AxiBusAddrWidth-1:0] ), + .burst_len ( ax_blen_postinc ), + .is_a_read ( !rr_out_req_write ), + .phy_valid_i ( s_rx_valid ), + .phy_ready_o ( s_rx_ready ), + .data_i ( s_rx_data ), + .last_i ( s_rx_last ), + .error_i ( s_rx_error ), + .axi_valid_o ( ser_out_rsp.r_valid ), + .axi_ready_i ( ser_out_req.r_ready ), + .data_o ( s_r_split ) + ); + + `FFARN(merge_r_q,merge_r_d,1'b0,clk_i,rst_ni) + `FFARN(s_rx_data_lower_q,s_rx_data_lower_d,'0,clk_i,rst_ni) + + // ========================================================= + // W channel: Buffer. Cuts path and upsamples when needed + // ========================================================= + + assign w_data_fifo_in.data = ser_out_req.w.data; + assign w_data_fifo_in.strb = ser_out_req.w.strb; + assign w_data_fifo_in.last = ser_out_req.w.last; + assign w_data_fifo_in.user = ser_out_req.w.user; + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .T ( axi_w_chan_t ), + .DEPTH ( 8 ) + ) wchan_stream_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( w_data_fifo_in ), + .valid_i ( ser_out_req.w_valid ), + .ready_o ( ser_out_rsp.w_ready ), + .data_o ( w_data_fifo ), + .valid_o ( w_data_valid ), + .ready_i ( w_data_ready ) + ); + + hyperbus_w2phy #( + .AxiDataWidth ( AxiDataWidth ), + .T ( axi_w_chan_t ), + .NumPhys ( NumPhys ) + ) i_hyperbus_w2phy ( + .clk_i, + .rst_ni, + .size ( rr_out_req_ax.size ), + .is_a_write ( rr_out_req_write ), + .trans_handshake ( trans_handshake ), + .start_addr ( rr_out_req_ax.addr[AxiBusAddrWidth-1:0] ), + .data_i ( w_data_fifo ), + .axi_valid_i ( w_data_valid ), + .axi_ready_o ( w_data_ready ), + .data_o ( s_tx_data ), + .last_o ( s_tx_last ), + .strb_o ( s_tx_strb ), + .phy_valid_o ( s_tx_valid ), + .phy_ready_i ( s_tx_ready ) + ); + + always_comb begin + tx_o.data = s_tx_data; + tx_o.strb = s_tx_strb; + tx_o.last = s_tx_last; + tx_valid_o = s_tx_valid; + s_tx_ready = tx_ready_i; + split_w_d = split_w_q; + if( (NumPhys==2) & (~phys_in_use_i) ) begin + if(s_tx_valid & tx_ready_i) begin + split_w_d = split_w_q+1; + end + tx_o.last = s_tx_last & split_w_q; + tx_valid_o = s_tx_valid; + s_tx_ready = tx_ready_i & split_w_q; + tx_o.data = { s_tx_data[PhyDataWidth/2-1:0] , s_tx_data[PhyDataWidth/2-1:0] }; + tx_o.strb = { s_tx_strb[NumPhys-1:0] , s_tx_strb[NumPhys-1:0] }; + if(split_w_q) begin + tx_o.data = { s_tx_data[PhyDataWidth-1:PhyDataWidth/2] , s_tx_data[PhyDataWidth-1:PhyDataWidth/2] }; + tx_o.strb = { s_tx_strb[NumPhys*2-1:NumPhys] , s_tx_strb[NumPhys*2-1:NumPhys] }; + end + end + end + + `FFARN(split_w_q,split_w_d,1'b0,clk_i,rst_ni) + + // ============================ + // B channel: passthrough + // ============================ + + assign ser_out_rsp.b.resp = b_error_i ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY; + assign ser_out_rsp.b.user = '0; + assign ser_out_rsp.b.id = '0; + assign ser_out_rsp.b_valid = b_valid_i; + assign b_ready_o = ser_out_req.b_ready; + + // ============================ + // Transfer status + // ============================ + + assign trans_active_o = trans_active_q; + + assign trans_active_set = trans_handshake; + assign trans_active_reset = (rx_valid_i & rx_ready_o & rx_i.last) | (b_valid_i & b_ready_o); + + // Allow W transfers iff currently engaged in write (AW already received) + assign trans_wready_set = trans_active_set & rr_out_req_write; + assign trans_wready_reset = (tx_valid_o & tx_ready_i & tx_o.last); + + // Set overrules reset as a transfer must start before it finishes + always_comb begin : proc_comb_trans_active + trans_active_d = trans_active_q; + trans_wready_d = trans_wready_q; + if (trans_active_reset) trans_active_d = 1'b0; + if (trans_active_set) trans_active_d = 1'b1; + if (trans_wready_reset) trans_wready_d = 1'b0; + if (trans_wready_set) trans_wready_d = 1'b1; + end + + // ========================= + // Registers + // ========================= + + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff + if(~rst_ni) begin + trans_active_q <= '0; + trans_wready_q <= '0; + end else begin + trans_active_q <= trans_active_d; + trans_wready_q <= trans_wready_d; + end + end + + // ========================= + // Assertions + // ========================= + + // pragma translate_off + `ifndef VERILATOR + initial assert (AxiDataWidth >= 16 && AxiDataWidth <= 1024) + else $error("AxiDatawidth must be a power of two within [16, 1024]."); + +// access_16b_align : assert property( +// @(posedge clk_i) trans_handshake & (rr_out_req_ax.size != '0) |-> (rr_out_req_ax.addr[0] == 1'b0)) +// else $fatal (1, "The address of a non-byte-size access must be 2-byte aligned."); + + burst_type : assert property( + @(posedge clk_i) trans_handshake |-> ( (rr_out_req_ax.burst == axi_pkg::BURST_INCR) || ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)) ) ) + else $fatal (1, "Non-incremental burst passed; this is currently not supported."); + `endif + // pragma translate_on + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_cfg_regs.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_cfg_regs.sv new file mode 100644 index 000000000..0ee578ecf --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_cfg_regs.sv @@ -0,0 +1,133 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Paul Scheffler + +module hyperbus_cfg_regs #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = -1, + parameter int unsigned RegDataWidth = -1, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type rule_t = logic, + parameter logic [RegDataWidth-1:0] RstChipBase = -1, // Base address for all chips + parameter logic [RegDataWidth-1:0] RstChipSpace = -1, // 64 KiB: Current maximum HyperBus device size + parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys) +) ( + input logic clk_i, + input logic rst_ni, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output hyperbus_pkg::hyper_cfg_t cfg_o, + output rule_t [NumChips-1:0] chip_rules_o, + input trans_active_i +); + `include "common_cells/registers.svh" + + // Internal Parameters + localparam int unsigned NumBaseRegs = 11; + localparam int unsigned NumRegs = 2*NumChips + NumBaseRegs; + localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); + localparam int unsigned RegStrbWidth = RegDataWidth/8; // TODO ASSERT: Must be power of two >= 16!! + + // Data and index types + typedef logic [RegsBits-1:0] reg_idx_t; + typedef logic [RegDataWidth-1:0] reg_data_t; + + // Local signals + hyperbus_pkg::hyper_cfg_t cfg_d, cfg_q; + reg_data_t [NumChips-1:0][1:0] crange_d, crange_q, crange_rstval; + reg_idx_t sel_reg; + logic sel_reg_mapped; + reg_data_t wmask; + + assign sel_reg = reg_req_i.addr[$clog2(RegStrbWidth) +: RegsBits]; + assign sel_reg_mapped = (sel_reg < NumRegs); + + assign reg_rsp_o.ready = ~trans_active_i; // Config writeable unless currently in transfer + assign reg_rsp_o.error = ~sel_reg_mapped; + + // Read from register + reg_data_t [NumRegs-1:0] rfield; + always_comb begin : proc_comb_read + rfield = '0; + reg_rsp_o.rdata = '0; + if (sel_reg_mapped) begin + rfield = { + crange_q, + reg_data_t'(cfg_q.t_csh_cycles), + reg_data_t'(cfg_q.which_phy), + reg_data_t'(cfg_q.phys_in_use), + reg_data_t'(cfg_q.address_space), + reg_data_t'(cfg_q.address_mask_msb), + reg_data_t'(cfg_q.t_tx_clk_delay), + reg_data_t'(cfg_q.t_rx_clk_delay), + reg_data_t'(cfg_q.t_read_write_recovery), + reg_data_t'(cfg_q.t_burst_max), + reg_data_t'(cfg_q.en_latency_additional), + reg_data_t'(cfg_q.t_latency_access) + }; + reg_rsp_o.rdata = rfield[sel_reg]; + end + end + + // Generate write mask + for (genvar i = 0; unsigned'(i) < RegStrbWidth; ++i ) begin : gen_wmask + assign wmask[8*i +: 8] = {8{reg_req_i.wstrb[i]}}; + end + + // Write to register + always_comb begin : proc_comb_write + logic chip_reg; + logic [$clog2(NumChips)-1:0] sel_chip; + cfg_d = cfg_q; + crange_d = crange_q; + if (reg_req_i.valid & reg_req_i.write & sel_reg_mapped) begin + case (sel_reg) + 'h0: cfg_d.t_latency_access = (~wmask & cfg_q.t_latency_access ) | (wmask & reg_req_i.wdata); + 'h1: cfg_d.en_latency_additional = (~wmask & cfg_q.en_latency_additional ) | (wmask & reg_req_i.wdata); + 'h2: cfg_d.t_burst_max = (~wmask & cfg_q.t_burst_max ) | (wmask & reg_req_i.wdata); + 'h3: cfg_d.t_read_write_recovery = (~wmask & cfg_q.t_read_write_recovery ) | (wmask & reg_req_i.wdata); + 'h4: cfg_d.t_rx_clk_delay = (~wmask & cfg_q.t_rx_clk_delay ) | (wmask & reg_req_i.wdata); + 'h5: cfg_d.t_tx_clk_delay = (~wmask & cfg_q.t_tx_clk_delay ) | (wmask & reg_req_i.wdata); + 'h6: cfg_d.address_mask_msb = (~wmask & cfg_q.address_mask_msb ) | (wmask & reg_req_i.wdata); + 'h7: cfg_d.address_space = (~wmask & cfg_q.address_space ) | (wmask & reg_req_i.wdata); + 'h8: cfg_d.phys_in_use = (NumPhys==1) ? 0 : ( (~wmask & cfg_q.phys_in_use ) | (wmask & reg_req_i.wdata) ); + 'h9: cfg_d.which_phy = (NumPhys==1) ? 0 : ( (~wmask & cfg_q.which_phy ) | (wmask & reg_req_i.wdata) ); + 'ha: cfg_d.t_csh_cycles = (~wmask & cfg_q.t_csh_cycles ) | (wmask & reg_req_i.wdata); + default: begin + {sel_chip, chip_reg} = sel_reg - NumBaseRegs; + crange_d[sel_chip][chip_reg] = (~wmask & crange_q[sel_chip][chip_reg]) | (wmask & reg_req_i.wdata); + end + endcase // sel_reg + end + end + + for (genvar i = 0; unsigned'(i) < NumChips; i++) begin : gen_crange_rstval + assign crange_rstval[i][0] = RstChipBase + (RstChipSpace * i); + assign crange_rstval[i][1] = RstChipBase + (RstChipSpace * (i+1)); // Address decoder: end noninclusive + end + + // Registers + `FFARN(cfg_q, cfg_d, RstCfg, clk_i, rst_ni); + `FFARN(crange_q, crange_d, crange_rstval, clk_i, rst_ni); + + // Outputs + assign cfg_o = cfg_q; + for (genvar i = 0; unsigned'(i) < NumChips; ++i) begin : gen_crange_out + assign chip_rules_o[i].idx = unsigned'(i); // No overlap: keep indices sequential + assign chip_rules_o[i].start_addr = crange_q[i][0]; + assign chip_rules_o[i].end_addr = crange_q[i][1]; + end + + // pragma translate_off + `ifndef VERILATOR + initial assert (RegDataWidth >= 16 && $countones(RegDataWidth) == 1) + else $error("RegDataWidth must be a power of two bigger than 16."); + `endif + // pragma translate_on + +endmodule : hyperbus_cfg_regs diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_clk_gen.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_clk_gen.sv new file mode 100644 index 000000000..546ca8669 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_clk_gen.sv @@ -0,0 +1,58 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Hayate Okuhara + +/// Generates 4 phase shifted clocks out of one faster clock +module hyperbus_clk_gen ( + input logic clk_i, // input clock + input logic rst_ni, + output logic clk0_o, // have the input clock - 0deg phase shift + output logic clk90_o, // have the input clock - 90deg phase shift + output logic clk180_o, // have the input clock - 180deg phase shift + output logic clk270_o, // have the input clock - 270deg phase shift + output logic rst_no +); + + + logic r_clk0_o; + logic r_clk90_o; + logic r_clk180_o; + logic r_clk270_o; + + logic s_clk0_n; + + assign clk0_o = r_clk0_o; + assign clk90_o = r_clk90_o; + assign clk180_o = r_clk180_o; + assign clk270_o = r_clk270_o; + assign rst_no = rst_ni; + + tc_clk_inverter i_clk0_inverter ( + .clk_i (r_clk0_o), + .clk_o (s_clk0_n) + ); + + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + r_clk0_o <= 0; + r_clk180_o <= 1; + end else begin + r_clk0_o <= s_clk0_n; + r_clk180_o <= r_clk90_o; + end + end + + always_ff @(negedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + r_clk90_o <= 0; + r_clk270_o <= 1; + end else begin + r_clk90_o <= r_clk0_o; + r_clk270_o <= r_clk180_o; + end + end + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_clock_diff_out.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_clock_diff_out.sv new file mode 100644 index 000000000..e567cfa80 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_clock_diff_out.sv @@ -0,0 +1,46 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Armin Berger +// Stephan Keck + +/// A Hyperbus differential clock output generator. +module hyperbus_clock_diff_out +( + input logic in_i, + input logic en_i, //high enable + output logic out_o, + output logic out_no +); + + `ifdef FPGA_SYNTHESIS + + logic en_sync; + + always_latch + begin + if (in_i == 1'b0) + en_sync <= en_i; + end + + assign out_o = in_i & en_sync; + assign out_no = ~out_o; + + `else + + tc_clk_gating i_hyper_ck_gating ( + .clk_i ( in_i ), + .en_i ( en_i ), + .test_en_i ( 1'b0 ), + .clk_o ( out_o ) + ); + + tc_clk_inverter i_hyper_ck_no_inv ( + .clk_i ( out_o ), + .clk_o ( out_no ) + ); + + `endif // !`ifdef FPGA_SYNTHESIS + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_ddr_out.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_ddr_out.sv new file mode 100644 index 000000000..d737d157b --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_ddr_out.sv @@ -0,0 +1,47 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Armin Berger +// Stephan Keck + +module hyperbus_ddr_out #( + parameter logic Init = 1'b0 +)( + input logic clk_i, + input logic rst_ni, + input logic d0_i, + input logic d1_i, + output logic q_o +); + logic q0; + logic q1; + +`ifdef FPGA_EMUL + always_comb + begin + if(clk_i == 1'b0) + q_o = q1; + else + q_o = q0; + end +`else + tc_clk_mux2 i_ddrmux ( + .clk_o ( q_o ), + .clk0_i ( q1 ), + .clk1_i ( q0 ), + .clk_sel_i ( clk_i ) + ); +`endif // !`ifdef FPGA_EMUL + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + q0 <= Init; + q1 <= Init; + end else begin + q0 <= d0_i; + q1 <= d1_i; + end + end + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_delay.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_delay.sv new file mode 100644 index 000000000..e7b885320 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_delay.sv @@ -0,0 +1,25 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Thomas Benz +// Paul Scheffler + +module hyperbus_delay ( + input logic in_i, + input logic [3:0] delay_i, + output logic out_o +); + + configurable_delay #( + .NUM_STEPS(16) + ) i_delay ( + .clk_i ( in_i ), + `ifndef TARGET_ASIC + .enable_i ( 1'b1 ), + `endif + .delay_i ( delay_i ), + .clk_o ( out_o ) + ); + +endmodule : hyperbus_delay diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_phy.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_phy.sv new file mode 100644 index 000000000..8a77b6f0b --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_phy.sv @@ -0,0 +1,390 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Armin Berger +// Stephan Keck +// Thomas Benz +// Paul Scheffler + +module hyperbus_phy import hyperbus_pkg::*; #( + parameter int unsigned NumChips = 2, + parameter int unsigned NumPhys = -1, + parameter int unsigned TimerWidth = 16, + parameter int unsigned RxFifoLogDepth = 3, + parameter int unsigned SyncStages = 2, + parameter int unsigned StartupCycles = 300 /*us*/ * 200 /*MHz*/ // Conservative maximum frequency estimate +)( + input logic clk_i, + input logic clk_i_90, + input logic rst_ni, + input logic test_mode_i, + // Config registers + input hyper_cfg_t cfg_i, + // Transactions + input logic trans_valid_i, + output logic trans_ready_o, + input hyper_tf_t trans_i, // TODO: increase burst width! + input logic [NumChips-1:0] trans_cs_i, + // Transmitting channel + input logic tx_valid_i, + output logic tx_ready_o, + input logic [15:0] tx_data_i, + input logic [1:0] tx_strb_i, + // Receiving channel + output logic rx_valid_o, + input logic rx_ready_i, + output logic [15:0] rx_data_o, + output logic rx_error_o, + output logic rx_last_o, + // B response + output logic b_valid_o, + input logic b_ready_i, + output logic b_error_o, + // Physical interface + output logic [NumChips-1:0] hyper_cs_no, + output logic hyper_ck_o, + output logic hyper_ck_no, + output logic hyper_rwds_o, + input logic hyper_rwds_i, + output logic hyper_rwds_oe_o, + input logic [7:0] hyper_dq_i, + output logic [7:0] hyper_dq_o, + output logic hyper_dq_oe_o, + output logic hyper_reset_no +); + + logic [1:0] phys_in_use; + + assign phys_in_use = (NumPhys==2) ? (cfg_i.phys_in_use + 1) : 1; + + // PHY state + hyper_phy_state_t state_d, state_q; + logic [TimerWidth-1:0] timer_d, timer_q; + hyper_tf_t tf_d, tf_q; + logic [NumChips-1:0] cs_d, cs_q; + + // Whether B response is pending + logic b_pending_q; + logic b_pending_set; + logic b_pending_clear; + + // How many R response words are outstanding if any + logic [RxFifoLogDepth:0] r_outstand_q; + logic r_outstand_inc; + logic r_outstand_dec; + + // Auxiliar control signals + logic ctl_write_zero_lat; + logic ctl_add_latency; + logic ctl_tf_burst_last; + logic ctl_tf_burst_done; + logic ctl_timer_two; + logic ctl_timer_one; + logic ctl_timer_zero; + logic ctl_timer_rwr_done; + logic ctl_rclk_ena; + logic ctl_wclk_ena; + + // Command-address + hyper_phy_ca_t ca; + + // Transciever IO + logic trx_clk_ena; + logic trx_cs_ena; + logic trx_rwds_sample; + logic trx_rwds_sample_ena; + logic [15:0] trx_tx_data; + logic trx_tx_data_oe; + logic [1:0] trx_tx_rwds; + logic trx_tx_rwds_oe; + logic trx_rx_clk_set; + logic trx_rx_clk_reset; + logic [15:0] trx_rx_data; + logic trx_rx_valid; + logic trx_rx_ready; + + // ================= + // Transciever + // ================= + + hyperbus_trx #( + .NumChips ( NumChips ), + .RxFifoLogDepth ( RxFifoLogDepth ), + .SyncStages ( SyncStages ) + ) i_trx ( + .clk_i, + .clk_i_90, + .rst_ni, + .test_mode_i, + .cs_i ( cs_q ), + .cs_ena_i ( trx_cs_ena ), + .rwds_sample_o ( trx_rwds_sample ), + .rwds_sample_ena_i ( trx_rwds_sample_ena ), + .tx_clk_ena_i ( trx_clk_ena ), + .tx_data_i ( trx_tx_data ), + .tx_data_oe_i ( trx_tx_data_oe ), + .tx_rwds_i ( trx_tx_rwds ), + .tx_rwds_oe_i ( trx_tx_rwds_oe ), + .rx_clk_delay_i ( cfg_i.t_rx_clk_delay ), + .rx_clk_set_i ( trx_rx_clk_set ), + .rx_clk_reset_i ( trx_rx_clk_reset ), + .rx_data_o ( trx_rx_data ), + .rx_valid_o ( trx_rx_valid ), + .rx_ready_i ( trx_rx_ready ), + .hyper_cs_no, + .hyper_ck_o, + .hyper_ck_no, + .hyper_rwds_o, + .hyper_rwds_i, + .hyper_rwds_oe_o, + .hyper_dq_i, + .hyper_dq_o, + .hyper_dq_oe_o, + .hyper_reset_no + ); + + // ============== + // Dataflow + // ============== + + // Command-address + assign ca = hyper_phy_ca_t '{ + write: ~tf_q.write, + addr_space: tf_q.address_space, + burst_type: tf_q.burst_type, + addr_upper: tf_q.address[31:3], + reserved: '0, + addr_lower: tf_q.address[2:0] + }; + + // Write dataflow + always_comb begin : proc_comb_tx + trx_tx_data = '0; + trx_tx_rwds = '0; + tx_ready_o = 1'b0; + ctl_wclk_ena = 1'b0; + if (state_q == SendCA) begin + // In CA phase: use timer to select word + trx_tx_data = ca[(8'(timer_q) << 4) +: 16]; + end else if (state_q == Write) begin + trx_tx_data = tx_data_i; + trx_tx_rwds = ~tx_strb_i; + tx_ready_o = 1'b1; // Memory always ready within HyperBus burst + ctl_wclk_ena = tx_valid_i; + end + end + + // Write response dataflow + assign b_valid_o = b_pending_q; + assign b_error_o = 1'b0; // TODO + assign b_pending_clear = b_valid_o & b_ready_i; + + // FF indicating whether B response pending + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_b_pending + if (~rst_ni) b_pending_q <= 1'b0; + else if (b_pending_set) b_pending_q <= 1'b1; + else if (b_pending_clear) b_pending_q <= 1'b0; + end + + // Read response dataflow + assign rx_data_o = trx_rx_data; + assign rx_error_o = 1'b0; + assign rx_last_o = (state_q != Read) & ctl_tf_burst_done & (r_outstand_q == 1); + + assign trx_rx_ready = rx_ready_i; + assign rx_valid_o = trx_rx_valid & (r_outstand_q != '0); + // Suspend clock one cycle for every stall caused by upstream. + // This ensures that a sufficiently large RX FIFO will not overflow. + assign ctl_rclk_ena = ~(rx_valid_o & ~rx_ready_i); + // Disable incoming RWDS clock enable once all words received + assign trx_rx_clk_reset = b_pending_clear; + + // Counter for outstanding R responses + assign r_outstand_dec = rx_valid_o & rx_ready_i; + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_r_outstand + if (~rst_ni) r_outstand_q <= '0; + else if (r_outstand_inc & ~r_outstand_dec) r_outstand_q <= r_outstand_q + 1; + else if (r_outstand_dec & ~r_outstand_inc) r_outstand_q <= r_outstand_q - 1; + end + + // ============= + // Control + // ============= + assign ctl_write_zero_lat = tf_q.address_space & tf_q.write; + // cfg_i.latency_addentional overwrites the trx_rwds_sample. Be careful. + assign ctl_add_latency = trx_rwds_sample | cfg_i.en_latency_additional; + + assign ctl_tf_burst_last = (tf_q.burst == 1) || (tf_q.burst == phys_in_use); + assign ctl_tf_burst_done = (tf_q.burst == 0); + + assign ctl_timer_rwr_done = (timer_q <= 3); + assign ctl_timer_two = (timer_q == 2); + assign ctl_timer_one = (timer_q == 1); + assign ctl_timer_zero = (timer_q == 0); + + // FSM logic + always_comb begin : proc_comb_phy_fsm + // Default outputs + trans_ready_o = 1'b0; + r_outstand_inc = 1'b0; + b_pending_set = 1'b0; + trx_cs_ena = 1'b1; + trx_clk_ena = 1'b0; + trx_rx_clk_set = 1'b0; + trx_rwds_sample_ena = 1'b0; + // Default next state + state_d = state_q; + timer_d = timer_q - 1; + tf_d = tf_q; + cs_d = cs_q; + // Tri-state control of dq and rwds + trx_tx_rwds_oe = 1'b0; + trx_tx_data_oe = 1'b0; + // State-dependent logic + case (state_q) + Startup: begin + trx_cs_ena = 1'b0; + // Timer resets to parameterized startup delay + if (ctl_timer_one) begin + state_d = Idle; + end + end + Idle: begin + trx_cs_ena = 1'b0; + timer_d = timer_q; + // Signal ready for, pop next transfer if Write response sent + trans_ready_o = 1'b1; + if (trans_valid_i & ~b_pending_q & r_outstand_q == '0) begin + tf_d = trans_i; + cs_d = trans_cs_i; + // Send 3 CA words (t_CSS respected through clock delay) + timer_d = 2; + state_d = SendCA; + // Enable output driver (needs to be enabled one cycle + // earlier since tri-state enables of IO pads are quite + // slow compared to the data pins) + trx_tx_data_oe = 1'b1; + end + end + SendCA: begin + // Dataflow handled outside FSM + trx_clk_ena = 1'b1; + trx_tx_data_oe = 1'b1; + trx_rwds_sample_ena = ~ctl_write_zero_lat; + if (ctl_timer_zero) begin + if (ctl_write_zero_lat) begin + timer_d = cfg_i.t_burst_max; + state_d = Write; + end else begin + timer_d = TimerWidth'(cfg_i.t_latency_access) << ctl_add_latency; + state_d = WaitLatAccess; + end + end + end + WaitLatAccess: begin + trx_clk_ena = 1'b1; + trx_tx_data_oe = 1'b1; + // Substract cycle for last CA and another for state delay + if (ctl_timer_two) begin + timer_d = cfg_i.t_burst_max; + // Switch to write or read phase and already start + // turnaround of tri-state driver (depending on latency + // config and if read or write transaction). + if (tf_q.write) begin + state_d = Write; + trx_tx_data_oe = 1'b1; + // For zero latency writes, we must not drive the RWDS + // signal (see specs page 9). Depending on the latency + // mode we thus drive only the DQ signals or DQ + RWDS. + trx_tx_rwds_oe = ~ctl_write_zero_lat; + end else begin + state_d = Read; + trx_tx_data_oe = 1'b0; + trx_tx_rwds_oe = 1'b0; + end + end + end + Read: begin + // Dataflow handled outside FSM + trx_rx_clk_set = 1'b1; + if (ctl_rclk_ena) begin + trx_clk_ena = 1'b1; + r_outstand_inc = 1'b1; + tf_d.burst = tf_q.burst - phys_in_use; + tf_d.address = tf_q.address + 1; + if (ctl_tf_burst_last) begin + timer_d = cfg_i.t_csh_cycles; + state_d = WaitXfer; + end + end + // Force-terminate access on burst time limit + if (ctl_timer_one) begin + timer_d = cfg_i.t_csh_cycles; + state_d = WaitXfer; + end + end + Write: begin + // Drive DQ lines in write mode + trx_tx_data_oe = 1'b1; + // For zero-latency writes we must not use RWDS as mask signal + trx_tx_rwds_oe = ~ctl_write_zero_lat; + // Dataflow handled outside FSM + if (ctl_wclk_ena) begin + trx_clk_ena = 1'b1; + tf_d.burst = tf_q.burst - phys_in_use; + tf_d.address = tf_q.address + 1; + if (ctl_tf_burst_last) begin + b_pending_set = 1'b1; + timer_d = cfg_i.t_csh_cycles; + state_d = WaitXfer; + end + end + // Force-terminate access on burst time limit + if (ctl_timer_one) begin + timer_d = cfg_i.t_csh_cycles; + state_d = WaitXfer; + end + end + WaitXfer: begin + // Wait for FFed Clock and output to stop + // May have to be prolonged for potential future devices with t_CSH > 0 + if (ctl_timer_zero) begin + timer_d = cfg_i.t_read_write_recovery; + state_d = WaitRWR; + end + end + WaitRWR: begin + trx_cs_ena = 1'b0; + if (ctl_timer_rwr_done) begin + if (ctl_tf_burst_done) begin + state_d = Idle; + end else begin + state_d = SendCA; + // Re-enable the io driver if we immediately start the + // next transaction. + trx_tx_data_oe = 1'b1; + end + end + end + default: begin + end + endcase + end + + // PHY state registers, including timer and transfer + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_phy + if (~rst_ni) begin + state_q <= Startup; + timer_q <= StartupCycles; + tf_q <= hyper_tf_t'{burst_type: 1'b1, default:'0}; + cs_q <= '0; + end else begin + state_q <= state_d; + timer_q <= timer_d; + tf_q <= tf_d; + cs_q <= cs_d; + end + end + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_phy2r.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_phy2r.sv new file mode 100644 index 000000000..902a46fe8 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_phy2r.sv @@ -0,0 +1,156 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Luca Valente + +module hyperbus_phy2r #( + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned NumPhys = -1, + parameter type T = logic, + parameter int unsigned BurstLength = -1, + parameter int unsigned AddrWidth = $clog2(AxiDataWidth/8) +) ( + input logic clk_i, + input logic rst_ni, + input logic [2:0] size, + input logic is_a_read, + input logic trans_handshake, + input logic [AddrWidth-1:0] start_addr, + input logic [BurstLength-1:0] burst_len, + output logic axi_valid_o, + input logic axi_ready_i, + output T data_o, + input logic phy_valid_i, + output logic phy_ready_o, + input logic [16*NumPhys-1:0] data_i, + input logic last_i, + input logic error_i +); + + // Cutting the combinatorial path between cdc fifo and AXI Master + typedef enum logic [2:0] { + Idle, + WaitData, + Sample, + CntReady + } hyper_splitter_state_t; + + hyper_splitter_state_t state_d, state_q; + + localparam int unsigned NumAxiBytes = AxiDataWidth/8; + localparam int unsigned NumPhyBytes = NumPhys*2; + localparam int unsigned AxiBytesInPhyBeat = NumAxiBytes/NumPhyBytes; + localparam int unsigned WordCntWidth = (AxiBytesInPhyBeat==1) ? 1 : $clog2(AxiBytesInPhyBeat); + + logic [BurstLength-1:0] byte_axi_addr_d, byte_axi_addr_q; + logic [BurstLength-1:0] byte_phy_cnt_d, byte_phy_cnt_q; + logic [BurstLength-1:0] last_addr_d, last_addr_q; + + logic [3:0] size_d, size_q; + T data_buffer_d, data_buffer_q; + + logic [WordCntWidth-1:0] word_cnt; + logic enough_data; + logic sent_available_data; + logic [BurstLength-1:0] next_axi_addr; + + assign word_cnt = (AxiBytesInPhyBeat==1) ? '0 : byte_phy_cnt_q[($clog2(NumPhys)+1) +:WordCntWidth]; + assign next_axi_addr = ((byte_axi_addr_q>>size_d)<< size_d) + (1<= next_axi_addr; + assign sent_available_data = byte_axi_addr_d >= byte_phy_cnt_q; + assign data_o.data = data_buffer_q.data; + assign data_o.error = data_buffer_q.error; + assign data_o.valid = '0; + assign data_o.last = data_buffer_q.last && (last_addr_q==byte_axi_addr_d); + + always_comb begin : counter + byte_axi_addr_d = byte_axi_addr_q; + size_d = size_q; + byte_phy_cnt_d = byte_phy_cnt_q; + last_addr_d = last_addr_q; + if (trans_handshake & is_a_read) begin + byte_axi_addr_d[BurstLength-1:AddrWidth] = '0; + byte_axi_addr_d[AddrWidth-1:0] = start_addr; + size_d = size; + byte_phy_cnt_d[BurstLength-1:AddrWidth] = '0; + byte_phy_cnt_d[AddrWidth-1:0] = (start_addr>>NumPhys)<>size)<>size_q)<< size_q) + (1<200 is outside the spec and is unlikely to work with any HyperBus devices + automatic hyper_cfg_t cfg = hyper_cfg_t'{ + t_latency_access: 'h6, + en_latency_additional: 'b0, + t_burst_max: ((MinFreqMhz*35)/10), // t_{csm}: At lowest legal clock (100 MHz) 3.5us (0.5us safety margin) + t_read_write_recovery: 'h6, + t_rx_clk_delay: 'h8, + t_tx_clk_delay: 'h8, + address_mask_msb: 'd25, // 26 bit addresses = 2^6*2^20B == 64 MB per chip (biggest availale as of now) + address_space: 'b0, + phys_in_use: NumPhys-1, + which_phy: NumPhys-1, + t_csh_cycles: 'h1 + }; + + return cfg; + endfunction + +endpackage diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_trx.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_trx.sv new file mode 100644 index 000000000..1a2ac139f --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_trx.sv @@ -0,0 +1,227 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Paul Scheffler +// Armin Berger +// Stephan Keck + +module hyperbus_trx #( + parameter int unsigned NumChips = 2, + parameter int unsigned RxFifoLogDepth = 3, + parameter int unsigned SyncStages = 2 +)( + // Global signals + input logic clk_i, + input logic clk_i_90, + input logic rst_ni, + input logic test_mode_i, + // Transciever control: facing controller + input logic [NumChips-1:0] cs_i, + input logic cs_ena_i, + output logic rwds_sample_o, + input logic rwds_sample_ena_i, + + input logic tx_clk_ena_i, + input logic [15:0] tx_data_i, + input logic tx_data_oe_i, + input logic [1:0] tx_rwds_i, + input logic tx_rwds_oe_i, + + input logic [3:0] rx_clk_delay_i, + input logic rx_clk_set_i, + input logic rx_clk_reset_i, + output logic [15:0] rx_data_o, + output logic rx_valid_o, + input logic rx_ready_i, + // Physical interace: facing HyperBus + output logic [NumChips-1:0] hyper_cs_no, + output logic hyper_ck_o, + output logic hyper_ck_no, + output logic hyper_rwds_o, + input logic hyper_rwds_i, + output logic hyper_rwds_oe_o, + input logic [7:0] hyper_dq_i, + output logic [7:0] hyper_dq_o, + output logic hyper_dq_oe_o, + output logic hyper_reset_no +); + + // 90-degree-shifted clocks generated with delay line + logic tx_clk_ena_q; + logic tx_clk_90; + logic rx_rwds_90; + + // Delayed clock enable synchronous with data + + // Intermediate RX signals for RWDS domain + logic rx_rwds_clk_ena; + logic rx_rwds_clk_orig; + logic rx_rwds_clk; + logic rx_rwds_soft_rst; + logic [15:0] rx_rwds_fifo_in; + logic rx_rwds_fifo_valid; + logic rx_rwds_fifo_ready; + logic rx_rwds_clk_n; + + // Feed through async reset + assign hyper_reset_no = rst_ni; + + // ================= + // TX + control + // ================= + + // Shift clock by 90 degrees + assign tx_clk_90 = clk_i_90; + + // 90deg-shifted differential output clock, sampling output bytes centrally + hyperbus_clock_diff_out i_clock_diff_out ( + .in_i ( tx_clk_90 ), + .en_i ( tx_clk_ena_q ), + .out_o ( hyper_ck_o ), + .out_no ( hyper_ck_no ) + ); + + // Synchronize output chip select to shifted differential output clock + always_ff @(posedge tx_clk_90 or negedge rst_ni) begin : proc_ff_tx_shift90 + if (~rst_ni) hyper_cs_no <= '1; + else hyper_cs_no <= cs_ena_i ? ~cs_i : '1; + end + + // Data output DDR converters + for (genvar i = 0; i <= 7; i++) begin: gen_ddr_tx_data + hyperbus_ddr_out #( + .Init ( 1'b0 ) + ) i_ddr_tx_data ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .d0_i ( tx_data_i [i+8] ), + .d1_i ( tx_data_i [i] ), + .q_o ( hyper_dq_o [i] ) + ); + end + + // RWDS output DDR converter + hyperbus_ddr_out #( + .Init ( 1'b0 ) + ) i_ddr_tx_rwds ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .d0_i ( tx_rwds_i [1] ), + .d1_i ( tx_rwds_i [0] ), + .q_o ( hyper_rwds_o ) + ); + + // Delay output, clock enables to be synchronous with DDR-converted data + // The delayed clock also ensures t_CSS is respected at the start, end of CS + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_tx_delay + if(~rst_ni) begin + hyper_rwds_oe_o <= 1'b0; + hyper_dq_oe_o <= 1'b0; + tx_clk_ena_q <= 1'b0; + end else begin + hyper_rwds_oe_o <= tx_rwds_oe_i; + hyper_dq_oe_o <= tx_data_oe_i; + tx_clk_ena_q <= tx_clk_ena_i; + end + end + + // Sample RWDS on demand for extra latency determination + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rwds_sample + if (~rst_ni) rwds_sample_o <= '0; + else if (rwds_sample_ena_i) rwds_sample_o <= hyper_rwds_i; + end + + // ======== + // RX + // ======== + + // Set and Reset RX clock enable + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rx_delay + if (~rst_ni) rx_rwds_clk_ena <= 1'b0; + else if (rx_clk_set_i) rx_rwds_clk_ena <= 1'b1; + else if (rx_clk_reset_i) rx_rwds_clk_ena <= 1'b0; + end + + // Shift RWDS clock by 90 degrees + hyperbus_delay i_delay_rx_rwds_90 ( + .in_i ( hyper_rwds_i ), + .delay_i ( rx_clk_delay_i ), + .out_o ( rx_rwds_90 ) + ); + + // Gate delayed RWDS clock with RX clock enable + tc_clk_gating i_rwds_in_clk_gate ( + .clk_i ( rx_rwds_90 ), + .en_i ( rx_rwds_clk_ena ), + .test_en_i ( test_mode_i ), + .clk_o ( rx_rwds_clk_orig ) + ); + + // Reset RX state on async reset or on gated clock (whenever inactive) + // TODO: is this safe? Replace with tech cells? + assign rx_rwds_soft_rst = ~rst_ni | (~rx_rwds_clk_ena & ~test_mode_i); + + // RX data is valid one cycle after each RX soft reset + always_ff @(posedge rx_rwds_clk or posedge rx_rwds_soft_rst) begin : proc_read_in_valid + if (rx_rwds_soft_rst) rx_rwds_fifo_valid <= 1'b0; + else rx_rwds_fifo_valid <= 1'b1; + end + + // If testing, replace gated RWDS clock with primary (PHY) clock; + // PHY clock itself may be flattened with system clock _outside_ hyperbus! +`ifdef FPGA_SYNTHESIS + assign rx_rwds_clk = rx_rwds_clk_orig; +`else + tc_clk_mux2 i_rx_rwds_clk_mux ( + .clk0_i ( rx_rwds_clk_orig ), + .clk1_i ( clk_i ), + .clk_sel_i ( test_mode_i ), + .clk_o ( rx_rwds_clk ) + ); +`endif + + logic [7:0] rx_rwds_fifo_in_lo, rx_rwds_fifo_in_hi; + + // Data input DDR conversion + assign rx_rwds_fifo_in_lo = hyper_dq_i; + always @(posedge rx_rwds_clk or posedge rx_rwds_soft_rst) begin : proc_ff_ddr_in + if(rx_rwds_soft_rst) rx_rwds_fifo_in_hi <= '0; + else rx_rwds_fifo_in_hi <= hyper_dq_i; + end + + //In Verilator blocked and non-blocking assignments to same variable are supported, thus using two variables + assign rx_rwds_fifo_in = {rx_rwds_fifo_in_hi, rx_rwds_fifo_in_lo}; + + tc_clk_inverter i_rwds_clk_inverter ( + .clk_i ( rx_rwds_clk ), + .clk_o ( rx_rwds_clk_n ) + ); + + // Cross input data from RWDS domain into system domain + cdc_fifo_gray #( + .T ( logic [15:0] ), + .LOG_DEPTH ( RxFifoLogDepth ), + .SYNC_STAGES( SyncStages ) + ) i_rx_rwds_cdc_fifo ( + // RWDS domain + .src_clk_i ( rx_rwds_clk_n ), + .src_rst_ni ( rst_ni ), + .src_data_i ( rx_rwds_fifo_in ), + .src_valid_i ( rx_rwds_fifo_valid ), + .src_ready_o ( rx_rwds_fifo_ready ), + // System domain + .dst_clk_i ( clk_i ), + .dst_rst_ni ( rst_ni ), + .dst_data_o ( rx_data_o ), + .dst_valid_o ( rx_valid_o ), + .dst_ready_i ( rx_ready_i ) + ); + + // assert that the FIFO does not drop data in simulation + `ifndef SYNTHESIS + always @(negedge rx_rwds_fifo_ready) assert(rx_rwds_fifo_ready) + else $error("%m: HyperBus RX FIFO must always be ready to receive data"); + `endif + +endmodule diff --git a/hw/vendor/pulp_platform_hyperbus/src/hyperbus_w2phy.sv b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_w2phy.sv new file mode 100644 index 000000000..92cfe4757 --- /dev/null +++ b/hw/vendor/pulp_platform_hyperbus/src/hyperbus_w2phy.sv @@ -0,0 +1,213 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Thomas Benz +// Paul Scheffler +// Luca Valente + +module hyperbus_w2phy #( + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned NumPhys = -1, + parameter type T = logic, + parameter int unsigned AddrWidth = $clog2(AxiDataWidth/8) +) ( + input logic clk_i, + input logic rst_ni, + input logic [2:0] size, + input logic [AddrWidth-1:0] start_addr, + input logic is_a_write, + input logic trans_handshake, + input logic axi_valid_i, + output logic axi_ready_o, + input T data_i, + output logic phy_valid_o, + input logic phy_ready_i, + output logic [16*NumPhys-1:0] data_o, + output logic last_o, + output logic [2*NumPhys-1:0] strb_o +); + + localparam int unsigned NumAxiBytes = AxiDataWidth/8; + localparam int unsigned NumPhyBytes = NumPhys*2; + localparam int unsigned AxiBytesInPhyBeat = NumAxiBytes/NumPhyBytes; + localparam int unsigned WordCntWidth = (AxiBytesInPhyBeat==1) ? 1 : $clog2(AxiBytesInPhyBeat); + // Cutting the combinatorial path between AXI master and cdc fifo + typedef enum logic [2:0] { + Idle, + Sample, + CntReady + } hyper_upsizer_state_t; + + hyper_upsizer_state_t state_d, state_q; + + typedef struct packed { + logic [AxiDataWidth/8-1:0] strb; + logic [AxiDataWidth-1:0] data; + logic last; + } w2phy_chan_t; + + w2phy_chan_t data_buffer_d, data_buffer_q; + + logic is_16_bw, is_8_bw; + logic upsize; + logic enough_data; + logic first_tx_d, first_tx_q; + + + logic [NumPhys*2-1:0] mask_strobe_d, mask_strobe_q; + logic [WordCntWidth-1:0] word_cnt; + logic [AddrWidth-1:0] byte_idx_d, byte_idx_q; + logic [3:0] size_d, size_q; + logic [AddrWidth-1:0] cnt_data_phy_d, cnt_data_phy_q; + logic keep_sending; + logic upsize_q; + + assign is_8_bw = (size_d == 0); + assign is_16_bw = (size_d == 1) ; + assign upsize = (is_16_bw && (NumPhys==2)) | is_8_bw ; + assign upsize_q = ( (size_q==1) && (NumPhys==2)) | (size_q==0) ; + assign enough_data = !upsize; + assign keep_sending = (size_d>($clog2(NumPhys)+1)) && (cnt_data_phy_d != byte_idx_q); + assign word_cnt = cnt_data_phy_q>>($clog2(NumPhys)+1); + + + assign data_o = data_buffer_q.data[(16*NumPhys)*word_cnt +:(16*NumPhys)]; + assign strb_o = data_buffer_q.strb[ (2*NumPhys)*word_cnt +: (2*NumPhys)] & mask_strobe_q; + assign last_o = data_buffer_q.last && (!keep_sending || upsize_q); + + always_comb begin : counter + byte_idx_d = byte_idx_q; + size_d = size_q; + cnt_data_phy_d = cnt_data_phy_q; + first_tx_d = first_tx_q; + if (trans_handshake & is_a_write) begin + byte_idx_d = start_addr; + size_d = size; + cnt_data_phy_d = (start_addr>>NumPhys)<>size_d)<< size_d) + (1<=NumPhys) begin + if (cnt_data_phy_d != byte_idx_q) begin + state_d = CntReady; + end else if (axi_valid_i) begin + axi_ready_o = 1'b1; + state_d = enough_data ? CntReady : Sample; + end else begin + state_d = Sample; + end + end else if (size_d + +module per2axi +#( + parameter NB_CORES = 4, + parameter PER_ADDR_WIDTH = 32, + parameter PER_ID_WIDTH = 5, + parameter AXI_ADDR_WIDTH = 32, + parameter AXI_DATA_WIDTH = 64, + parameter AXI_USER_WIDTH = 6, + parameter AXI_ID_WIDTH = 3, + parameter AXI_STRB_WIDTH = AXI_DATA_WIDTH/8 +) +( + input logic clk_i, + input logic rst_ni, + input logic test_en_i, + + // PERIPHERAL INTERCONNECT SLAVE + //*************************************** + //REQUEST CHANNEL + input logic per_slave_req_i, + input logic [PER_ADDR_WIDTH-1:0] per_slave_add_i, + input logic per_slave_we_i, + input logic [31:0] per_slave_wdata_i, + input logic [3:0] per_slave_be_i, + input logic [PER_ID_WIDTH-1:0] per_slave_id_i, + output logic per_slave_gnt_o, + + //RESPONSE CHANNEL + output logic per_slave_r_valid_o, + output logic per_slave_r_opc_o, + output logic [PER_ID_WIDTH-1:0] per_slave_r_id_o, + output logic [31:0] per_slave_r_rdata_o, + + // AXI4 MASTER + //*************************************** + // WRITE ADDRESS CHANNEL + output logic axi_master_aw_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] axi_master_aw_addr_o, + output logic [2:0] axi_master_aw_prot_o, + output logic [3:0] axi_master_aw_region_o, + output logic [7:0] axi_master_aw_len_o, + output logic [2:0] axi_master_aw_size_o, + output logic [1:0] axi_master_aw_burst_o, + output logic axi_master_aw_lock_o, + output logic [3:0] axi_master_aw_cache_o, + output logic [3:0] axi_master_aw_qos_o, + output logic [AXI_ID_WIDTH-1:0] axi_master_aw_id_o, + output logic [AXI_USER_WIDTH-1:0] axi_master_aw_user_o, + input logic axi_master_aw_ready_i, + + // READ ADDRESS CHANNEL + output logic axi_master_ar_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] axi_master_ar_addr_o, + output logic [2:0] axi_master_ar_prot_o, + output logic [3:0] axi_master_ar_region_o, + output logic [7:0] axi_master_ar_len_o, + output logic [2:0] axi_master_ar_size_o, + output logic [1:0] axi_master_ar_burst_o, + output logic axi_master_ar_lock_o, + output logic [3:0] axi_master_ar_cache_o, + output logic [3:0] axi_master_ar_qos_o, + output logic [AXI_ID_WIDTH-1:0] axi_master_ar_id_o, + output logic [AXI_USER_WIDTH-1:0] axi_master_ar_user_o, + input logic axi_master_ar_ready_i, + + // WRITE DATA CHANNEL + output logic axi_master_w_valid_o, + output logic [AXI_DATA_WIDTH-1:0] axi_master_w_data_o, + output logic [AXI_STRB_WIDTH-1:0] axi_master_w_strb_o, + output logic [AXI_USER_WIDTH-1:0] axi_master_w_user_o, + output logic axi_master_w_last_o, + input logic axi_master_w_ready_i, + + // READ DATA CHANNEL + input logic axi_master_r_valid_i, + input logic [AXI_DATA_WIDTH-1:0] axi_master_r_data_i, + input logic [1:0] axi_master_r_resp_i, + input logic axi_master_r_last_i, + input logic [AXI_ID_WIDTH-1:0] axi_master_r_id_i, + input logic [AXI_USER_WIDTH-1:0] axi_master_r_user_i, + output logic axi_master_r_ready_o, + + // WRITE RESPONSE CHANNEL + input logic axi_master_b_valid_i, + input logic [1:0] axi_master_b_resp_i, + input logic [AXI_ID_WIDTH-1:0] axi_master_b_id_i, + input logic [AXI_USER_WIDTH-1:0] axi_master_b_user_i, + output logic axi_master_b_ready_o, + + // BUSY SIGNAL + output logic busy_o + ); + + // SIGNAL DECLARATION + logic s_aw_valid; + logic [AXI_ADDR_WIDTH-1:0] s_aw_addr; + logic [2:0] s_aw_prot; + logic [3:0] s_aw_region; + logic [7:0] s_aw_len; + logic [2:0] s_aw_size; + logic [1:0] s_aw_burst; + logic s_aw_lock; + logic [3:0] s_aw_cache; + logic [3:0] s_aw_qos; + logic [AXI_ID_WIDTH-1:0] s_aw_id; + logic [AXI_USER_WIDTH-1:0] s_aw_user; + logic s_aw_ready; + + logic s_ar_valid; + logic [AXI_ADDR_WIDTH-1:0] s_ar_addr; + logic [2:0] s_ar_prot; + logic [3:0] s_ar_region; + logic [7:0] s_ar_len; + logic [2:0] s_ar_size; + logic [1:0] s_ar_burst; + logic s_ar_lock; + logic [3:0] s_ar_cache; + logic [3:0] s_ar_qos; + logic [AXI_ID_WIDTH-1:0] s_ar_id; + logic [AXI_USER_WIDTH-1:0] s_ar_user; + logic s_ar_ready; + + logic s_w_valid; + logic [AXI_DATA_WIDTH-1:0] s_w_data; + logic [AXI_STRB_WIDTH-1:0] s_w_strb; + logic [AXI_USER_WIDTH-1:0] s_w_user; + logic s_w_last; + logic s_w_ready; + + logic s_r_valid; + logic [AXI_DATA_WIDTH-1:0] s_r_data; + logic [1:0] s_r_resp; + logic s_r_last; + logic [AXI_ID_WIDTH-1:0] s_r_id; + logic [AXI_USER_WIDTH-1:0] s_r_user; + logic s_r_ready; + + logic s_b_valid; + logic [1:0] s_b_resp; + logic [AXI_ID_WIDTH-1:0] s_b_id; + logic [AXI_USER_WIDTH-1:0] s_b_user; + logic s_b_ready; + + logic s_trans_req; + logic [AXI_ID_WIDTH-1:0] s_trans_id; + logic [AXI_ADDR_WIDTH-1:0] s_trans_add; + + + // PER2AXI REQUEST CHANNEL + per2axi_req_channel + #( + .PER_ID_WIDTH ( PER_ID_WIDTH ), + .PER_ADDR_WIDTH ( PER_ADDR_WIDTH ), + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), + .AXI_USER_WIDTH ( AXI_USER_WIDTH ), + .AXI_ID_WIDTH ( AXI_ID_WIDTH ) + ) + req_channel_i + ( + .per_slave_req_i ( per_slave_req_i ), + .per_slave_add_i ( per_slave_add_i ), + .per_slave_we_i ( per_slave_we_i ), + .per_slave_wdata_i ( per_slave_wdata_i ), + .per_slave_be_i ( per_slave_be_i ), + .per_slave_id_i ( per_slave_id_i ), + .per_slave_gnt_o ( per_slave_gnt_o ), + + .axi_master_aw_valid_o ( s_aw_valid ), + .axi_master_aw_addr_o ( s_aw_addr ), + .axi_master_aw_prot_o ( s_aw_prot ), + .axi_master_aw_region_o ( s_aw_region ), + .axi_master_aw_len_o ( s_aw_len ), + .axi_master_aw_size_o ( s_aw_size ), + .axi_master_aw_burst_o ( s_aw_burst ), + .axi_master_aw_lock_o ( s_aw_lock ), + .axi_master_aw_cache_o ( s_aw_cache ), + .axi_master_aw_qos_o ( s_aw_qos ), + .axi_master_aw_id_o ( s_aw_id ), + .axi_master_aw_user_o ( s_aw_user ), + .axi_master_aw_ready_i ( s_aw_ready ), + + .axi_master_ar_valid_o ( s_ar_valid ), + .axi_master_ar_addr_o ( s_ar_addr ), + .axi_master_ar_prot_o ( s_ar_prot ), + .axi_master_ar_region_o ( s_ar_region ), + .axi_master_ar_len_o ( s_ar_len ), + .axi_master_ar_size_o ( s_ar_size ), + .axi_master_ar_burst_o ( s_ar_burst ), + .axi_master_ar_lock_o ( s_ar_lock ), + .axi_master_ar_cache_o ( s_ar_cache ), + .axi_master_ar_qos_o ( s_ar_qos ), + .axi_master_ar_id_o ( s_ar_id ), + .axi_master_ar_user_o ( s_ar_user ), + .axi_master_ar_ready_i ( s_ar_ready ), + + .axi_master_w_valid_o ( s_w_valid ), + .axi_master_w_data_o ( s_w_data ), + .axi_master_w_strb_o ( s_w_strb ), + .axi_master_w_user_o ( s_w_user ), + .axi_master_w_last_o ( s_w_last ), + .axi_master_w_ready_i ( s_w_ready ), + + .trans_req_o ( s_trans_req ), + .trans_id_o ( s_trans_id ), + .trans_add_o ( s_trans_add ) + ); + + // PER2AXI RESPONSE CHANNEL + per2axi_res_channel + #( + .PER_ID_WIDTH ( PER_ID_WIDTH ), + .PER_ADDR_WIDTH ( PER_ADDR_WIDTH ), + .AXI_ID_WIDTH ( AXI_ID_WIDTH ), + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), + .AXI_USER_WIDTH ( AXI_USER_WIDTH ) + ) + res_channel_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .per_slave_r_valid_o ( per_slave_r_valid_o ), + .per_slave_r_opc_o ( per_slave_r_opc_o ), + .per_slave_r_id_o ( per_slave_r_id_o ), + .per_slave_r_rdata_o ( per_slave_r_rdata_o ), + + .axi_master_r_valid_i ( s_r_valid ), + .axi_master_r_data_i ( s_r_data ), + .axi_master_r_resp_i ( s_r_resp ), + .axi_master_r_last_i ( s_r_last ), + .axi_master_r_id_i ( s_r_id ), + .axi_master_r_user_i ( s_r_user ), + .axi_master_r_ready_o ( s_r_ready ), + + .axi_master_b_valid_i ( s_b_valid ), + .axi_master_b_resp_i ( s_b_resp ), + .axi_master_b_id_i ( s_b_id ), + .axi_master_b_user_i ( s_b_user ), + .axi_master_b_ready_o ( s_b_ready ), + + .trans_req_i ( s_trans_req ), + .trans_id_i ( s_trans_id ), + .trans_add_i ( s_trans_add ) + ); + + // BUSY UNIT + per2axi_busy_unit busy_unit_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + // WRITE INTERFACE + .aw_sync_i ( s_aw_valid & s_aw_ready ), + .b_sync_i ( s_b_valid & s_b_ready ), + + // READ INTERFACE + .ar_sync_i ( s_ar_valid & s_ar_ready ), + .r_sync_i ( s_r_valid & s_r_ready & s_r_last ), + + // BUSY SIGNAL + .busy_o ( busy_o ) + ); + + // AXI WRITE ADDRESS CHANNEL BUFFER + axi_aw_buffer + #( + .ID_WIDTH ( AXI_ID_WIDTH ), + .ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .USER_WIDTH ( AXI_USER_WIDTH ), + .BUFFER_DEPTH ( NB_CORES ) + ) + aw_buffer_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_en_i ( test_en_i ), + + .slave_valid_i ( s_aw_valid ), + .slave_addr_i ( s_aw_addr ), + .slave_prot_i ( s_aw_prot ), + .slave_region_i ( s_aw_region ), + .slave_len_i ( s_aw_len ), + .slave_size_i ( s_aw_size ), + .slave_burst_i ( s_aw_burst ), + .slave_lock_i ( s_aw_lock ), + .slave_cache_i ( s_aw_cache ), + .slave_qos_i ( s_aw_qos ), + .slave_id_i ( s_aw_id ), + .slave_user_i ( s_aw_user ), + .slave_ready_o ( s_aw_ready ), + + .master_valid_o ( axi_master_aw_valid_o ), + .master_addr_o ( axi_master_aw_addr_o ), + .master_prot_o ( axi_master_aw_prot_o ), + .master_region_o ( axi_master_aw_region_o ), + .master_len_o ( axi_master_aw_len_o ), + .master_size_o ( axi_master_aw_size_o ), + .master_burst_o ( axi_master_aw_burst_o ), + .master_lock_o ( axi_master_aw_lock_o ), + .master_cache_o ( axi_master_aw_cache_o ), + .master_qos_o ( axi_master_aw_qos_o ), + .master_id_o ( axi_master_aw_id_o ), + .master_user_o ( axi_master_aw_user_o ), + .master_ready_i ( axi_master_aw_ready_i ) + ); + + // AXI READ ADDRESS CHANNEL BUFFER + axi_ar_buffer + #( + .ID_WIDTH ( AXI_ID_WIDTH ), + .ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .USER_WIDTH ( AXI_USER_WIDTH ), + .BUFFER_DEPTH ( NB_CORES ) + ) + ar_buffer_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_en_i ( test_en_i ), + + .slave_valid_i ( s_ar_valid ), + .slave_addr_i ( s_ar_addr ), + .slave_prot_i ( s_ar_prot ), + .slave_region_i ( s_ar_region ), + .slave_len_i ( s_ar_len ), + .slave_size_i ( s_ar_size ), + .slave_burst_i ( s_ar_burst ), + .slave_lock_i ( s_ar_lock ), + .slave_cache_i ( s_ar_cache ), + .slave_qos_i ( s_ar_qos ), + .slave_id_i ( s_ar_id ), + .slave_user_i ( s_ar_user ), + .slave_ready_o ( s_ar_ready ), + + .master_valid_o ( axi_master_ar_valid_o ), + .master_addr_o ( axi_master_ar_addr_o ), + .master_prot_o ( axi_master_ar_prot_o ), + .master_region_o ( axi_master_ar_region_o ), + .master_len_o ( axi_master_ar_len_o ), + .master_size_o ( axi_master_ar_size_o ), + .master_burst_o ( axi_master_ar_burst_o ), + .master_lock_o ( axi_master_ar_lock_o ), + .master_cache_o ( axi_master_ar_cache_o ), + .master_qos_o ( axi_master_ar_qos_o ), + .master_id_o ( axi_master_ar_id_o ), + .master_user_o ( axi_master_ar_user_o ), + .master_ready_i ( axi_master_ar_ready_i ) + ); + + // WRITE DATA CHANNEL BUFFER + axi_w_buffer + #( + .DATA_WIDTH ( AXI_DATA_WIDTH ), + .USER_WIDTH ( AXI_USER_WIDTH ), + .BUFFER_DEPTH ( NB_CORES ) + ) + w_buffer_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_en_i ( test_en_i ), + + .slave_valid_i ( s_w_valid ), + .slave_data_i ( s_w_data ), + .slave_strb_i ( s_w_strb ), + .slave_user_i ( s_w_user ), + .slave_last_i ( s_w_last ), + .slave_ready_o ( s_w_ready ), + + .master_valid_o ( axi_master_w_valid_o ), + .master_data_o ( axi_master_w_data_o ), + .master_strb_o ( axi_master_w_strb_o ), + .master_user_o ( axi_master_w_user_o ), + .master_last_o ( axi_master_w_last_o ), + .master_ready_i ( axi_master_w_ready_i ) + ); + + // READ DATA CHANNEL BUFFER + axi_r_buffer + #( + .ID_WIDTH ( AXI_ID_WIDTH ), + .DATA_WIDTH ( AXI_DATA_WIDTH ), + .USER_WIDTH ( AXI_USER_WIDTH ), + .BUFFER_DEPTH ( 2 ) + ) + r_buffer_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_en_i ( test_en_i ), + + .slave_valid_i ( axi_master_r_valid_i ), + .slave_data_i ( axi_master_r_data_i ), + .slave_resp_i ( axi_master_r_resp_i ), + .slave_user_i ( axi_master_r_user_i ), + .slave_id_i ( axi_master_r_id_i ), + .slave_last_i ( axi_master_r_last_i ), + .slave_ready_o ( axi_master_r_ready_o ), + + .master_valid_o ( s_r_valid ), + .master_data_o ( s_r_data ), + .master_resp_o ( s_r_resp ), + .master_user_o ( s_r_user ), + .master_id_o ( s_r_id ), + .master_last_o ( s_r_last ), + .master_ready_i ( s_r_ready ) + ); + + // WRITE RESPONSE CHANNEL BUFFER + axi_b_buffer + #( + .ID_WIDTH ( AXI_ID_WIDTH ), + .USER_WIDTH ( AXI_USER_WIDTH ), + .BUFFER_DEPTH ( 2 ) + ) + b_buffer_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_en_i ( test_en_i ), + + .slave_valid_i ( axi_master_b_valid_i ), + .slave_resp_i ( axi_master_b_resp_i ), + .slave_id_i ( axi_master_b_id_i ), + .slave_user_i ( axi_master_b_user_i ), + .slave_ready_o ( axi_master_b_ready_o ), + + .master_valid_o ( s_b_valid ), + .master_resp_o ( s_b_resp ), + .master_id_o ( s_b_id ), + .master_user_o ( s_b_user ), + .master_ready_i ( s_b_ready ) + ); + +endmodule diff --git a/hw/vendor/pulp_platform_per2axi/src/per2axi_busy_unit.sv b/hw/vendor/pulp_platform_per2axi/src/per2axi_busy_unit.sv new file mode 100644 index 000000000..315567e3f --- /dev/null +++ b/hw/vendor/pulp_platform_per2axi/src/per2axi_busy_unit.sv @@ -0,0 +1,72 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Davide Rossi + +module per2axi_busy_unit +( + input logic clk_i, + input logic rst_ni, + + // WRITE INTERFACE + input logic aw_sync_i, + input logic b_sync_i, + + // READ INTERFACE + input logic ar_sync_i, + input logic r_sync_i, + + // BUSY SIGNAL + output logic busy_o +); + + logic [3:0] s_aw_trans_count; + logic [3:0] s_ar_trans_count; + + //COUNTER FOR NUMBER OF AW TRANSACTIONS + always_ff @ (posedge clk_i, negedge rst_ni) + begin + if(rst_ni == 1'b0) + s_aw_trans_count <= '0; + else + if ( aw_sync_i == 1'b1 && b_sync_i == 1'b0 ) + s_aw_trans_count <= s_aw_trans_count+1; + else + if ( aw_sync_i == 1'b0 && b_sync_i == 1'b1 ) + s_aw_trans_count <= s_aw_trans_count-1; + else + s_aw_trans_count <= s_aw_trans_count; + end + + //COUNTER FOR NUMBER OF AR TRANSACTIONS + always_ff @ (posedge clk_i, negedge rst_ni) + begin + if(rst_ni == 1'b0) + s_ar_trans_count <= '0; + else + if ( ar_sync_i == 1'b1 && r_sync_i == 1'b0 ) + s_ar_trans_count <= s_ar_trans_count+1; + else + if ( ar_sync_i == 1'b0 && r_sync_i == 1'b1 ) + s_ar_trans_count <= s_ar_trans_count-1; + else + s_ar_trans_count <= s_ar_trans_count; + end + + // GENERATION OF BUSY SIGNAL + always_comb + begin + if ( s_ar_trans_count == 0 && s_aw_trans_count == 0 ) + busy_o = 1'b0; + else + busy_o = 1'b1; + end + +endmodule diff --git a/hw/vendor/pulp_platform_per2axi/src/per2axi_req_channel.sv b/hw/vendor/pulp_platform_per2axi/src/per2axi_req_channel.sv new file mode 100644 index 000000000..2e8455154 --- /dev/null +++ b/hw/vendor/pulp_platform_per2axi/src/per2axi_req_channel.sv @@ -0,0 +1,203 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Davide Rossi + +module per2axi_req_channel +#( + // PARAMETERS + parameter PER_ADDR_WIDTH = 32, + parameter PER_ID_WIDTH = 5, + parameter AXI_ADDR_WIDTH = 32, + parameter AXI_DATA_WIDTH = 64, + parameter AXI_USER_WIDTH = 6, + parameter AXI_ID_WIDTH = 3, + // LOCAL PARAMETERS --> DO NOT OVERRIDE + parameter AXI_STRB_WIDTH = AXI_DATA_WIDTH/8 // DO NOT OVERRIDE +) +( + // PERIPHERAL INTERCONNECT SLAVE + //*************************************** + //REQUEST CHANNEL + input logic per_slave_req_i, + input logic [PER_ADDR_WIDTH-1:0] per_slave_add_i, + input logic per_slave_we_i, + input logic [31:0] per_slave_wdata_i, + input logic [3:0] per_slave_be_i, + input logic [PER_ID_WIDTH-1:0] per_slave_id_i, + output logic per_slave_gnt_o, + + // AXI4 MASTER + //*************************************** + // WRITE ADDRESS CHANNEL + output logic axi_master_aw_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] axi_master_aw_addr_o, + output logic [2:0] axi_master_aw_prot_o, + output logic [3:0] axi_master_aw_region_o, + output logic [7:0] axi_master_aw_len_o, + output logic [2:0] axi_master_aw_size_o, + output logic [1:0] axi_master_aw_burst_o, + output logic axi_master_aw_lock_o, + output logic [3:0] axi_master_aw_cache_o, + output logic [3:0] axi_master_aw_qos_o, + output logic [AXI_ID_WIDTH-1:0] axi_master_aw_id_o, + output logic [AXI_USER_WIDTH-1:0] axi_master_aw_user_o, + input logic axi_master_aw_ready_i, + + // READ ADDRESS CHANNEL + output logic axi_master_ar_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] axi_master_ar_addr_o, + output logic [2:0] axi_master_ar_prot_o, + output logic [3:0] axi_master_ar_region_o, + output logic [7:0] axi_master_ar_len_o, + output logic [2:0] axi_master_ar_size_o, + output logic [1:0] axi_master_ar_burst_o, + output logic axi_master_ar_lock_o, + output logic [3:0] axi_master_ar_cache_o, + output logic [3:0] axi_master_ar_qos_o, + output logic [AXI_ID_WIDTH-1:0] axi_master_ar_id_o, + output logic [AXI_USER_WIDTH-1:0] axi_master_ar_user_o, + input logic axi_master_ar_ready_i, + + // WRITE DATA CHANNEL + output logic axi_master_w_valid_o, + output logic [AXI_DATA_WIDTH-1:0] axi_master_w_data_o, + output logic [AXI_STRB_WIDTH-1:0] axi_master_w_strb_o, + output logic [AXI_USER_WIDTH-1:0] axi_master_w_user_o, + output logic axi_master_w_last_o, + input logic axi_master_w_ready_i, + + // CONTROL SIGNALS + output logic trans_req_o, + output logic [AXI_ID_WIDTH-1:0] trans_id_o, + output logic [AXI_ADDR_WIDTH-1:0] trans_add_o +); + + integer i; + + // AXI REQUEST GENERATION + always_comb + begin + axi_master_ar_valid_o = 1'b0; + axi_master_aw_valid_o = 1'b0; + axi_master_w_valid_o = 1'b0; + axi_master_w_last_o = 1'b0; + + if (per_slave_req_i == 1'b1 && // REQUEST FROM PERIPHERAL INTERCONNECT + per_slave_we_i == 1'b0 && // WRITE OPERATION + axi_master_aw_ready_i == 1'b1 && // AXI WRITE ADDRESS CHANNEL AVAILABLE + axi_master_w_ready_i == 1'b1 ) // AXI WRITE DATA CHANNEL AVAILABLE + begin + axi_master_aw_valid_o = 1'b1; + axi_master_w_valid_o = 1'b1; + axi_master_w_last_o = 1'b1; + end + else + if (per_slave_req_i == 1'b1 && // REQUEST FROM PERIPHERAL INTERCONNECT + per_slave_we_i == 1'b1 && // READ OPERATION + axi_master_ar_ready_i == 1'b1) // AXI WRITE ADDRESS CHANNEL AVAILABLE + begin + axi_master_ar_valid_o = 1'b1; + end + end + + // AXI ADDRESS GENERATION + assign axi_master_aw_addr_o = per_slave_add_i; + assign axi_master_ar_addr_o = per_slave_add_i; + + // AXI ID GENERATION - ONEHOT TO BIN DECODING + always_comb + begin + axi_master_aw_id_o = '0; + axi_master_ar_id_o = '0; + for ( i=0; i= max_address_hex: + print(f"address {hex(address)} >= max_address {hex(max_address_hex)}") + break + filtered_lines.append(line) + + with open(output_filename, 'w') as output_file: + output_file.writelines(filtered_lines) + + print(f"Filtered Verilog saved to {output_filename}") + except FileNotFoundError: + print(f"Error: File '{input_filename}' not found.") + +if len(sys.argv) != 3: + print("Usage: python post_proc_hex.py ") + sys.exit(1) + +input_file = sys.argv[1] +output_file = sys.argv[2] + +filter_verilog(input_file, output_file) diff --git a/sw/applications/example_hyperbus/main.c b/sw/applications/example_hyperbus/main.c new file mode 100644 index 000000000..ca480fb09 --- /dev/null +++ b/sw/applications/example_hyperbus/main.c @@ -0,0 +1,196 @@ +#include "hyperbus.h" +#include +#include +#include "core_v_mini_mcu.h" +#include "x-heep.h" +#include "hyperbus_regs.h" + +#define WORD0 0x12345678 +#define WORD1 0x87654321 +#define WORD2 0xDEADC0DE +#define WORD3 0xDEADBEEF + + +//HyperRam works only on 32b +volatile int32_t __attribute__((section(".xheep_data_hyperram"))) hyperram_buffer_32[301]; +volatile int32_t __attribute__((section(".xheep_data_hyperram"))) hyperram_buffer_32_config_reg; + +#define TEST_SORT_ARRAY_32 + +void __attribute__((noinline)) feed_array_32(int32_t* a, int n){ + for(int i=n-1;i>=0;i--) + a[i] = n - i; +} + +void __attribute__((noinline)) bubbleSort_32(int32_t* a, int n) { + for (int i = 0; i < n-1; i++) { + for (int j = 0; j < n-i-1; j++) { + if (a[j] > a[j+1]) { + // Swap arr[j] and arr[j+1] + int32_t temp = a[j]; + a[j] = a[j+1]; + a[j+1] = temp; + } + } + } +} + +uint8_t __attribute__((noinline)) isSorted_32(int32_t* a, int n) { + for (int i = 0; i < n-1; i++) { + if (a[i] > a[i+1]) { + return -1; + } + } + return 0; +} + +int main() { + + uint8_t random_val = 6; + //this should be 2 times 36-40ns (2* tacc for en_latency additional, this is the default value in the Flash model) + uint16_t t_latency_access = 6 & HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_MASK; + uint8_t en_latency_additional = 1 & (0x1 << HYPERBUS_EN_LATENCY_ADDITIONAL_EN_LATENCY_ADDITIONAL_BIT); + uint16_t t_burst_max = random_val++ & HYPERBUS_T_BURST_MAX_T_BURST_MAX_MASK; + uint8_t t_read_write_recovery = random_val++ & HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_MASK; + uint8_t t_rx_clk_delay = random_val++ & HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_MASK; + uint8_t t_tx_clk_delay = random_val++ & HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_MASK; + uint8_t address_mask_msb = 24 & HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_MASK; //up to 16MB + uint8_t address_space = 0 & (0x1 << HYPERBUS_ADDRESS_SPACE_ADDRESS_SPACE_BIT); +#if HYPERRAMNUMPHY == 1 + uint8_t phys_in_use = 0; + uint8_t which_phy = 0; +#else + uint8_t phys_in_use = random_val++ & (0x1 << HYPERBUS_PHYS_IN_USE_PHYS_IN_USE_BIT); + uint8_t which_phy = random_val++ & (0x1 << HYPERBUS_WHICH_PHY_WHICH_PHY_BIT); +#endif + uint8_t t_csh_cycle = random_val++ & HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_MASK; + + hyperbus_set_t_latency_access(t_latency_access); + hyperbus_set_en_latency_additional(en_latency_additional); + hyperbus_set_t_burst_max(t_burst_max); + hyperbus_set_t_read_write_recovery(t_read_write_recovery); + hyperbus_set_t_rx_clk_delay(t_rx_clk_delay); + hyperbus_set_t_tx_clk_delay(t_tx_clk_delay); + hyperbus_set_address_mask_msb(address_mask_msb); + hyperbus_set_address_space(address_space); + hyperbus_set_phys_in_use(phys_in_use); + hyperbus_set_which_phy(which_phy); + hyperbus_set_t_csh_cycle(t_csh_cycle); + + if (hyperbus_get_t_latency_access() != t_latency_access) { return -1; } + if(hyperbus_get_en_latency_additional() != en_latency_additional) { return -2; } + if(hyperbus_get_t_burst_max() != t_burst_max) { return -3; } + if(hyperbus_get_t_read_write_recovery() != t_read_write_recovery) { return -4; } + if(hyperbus_get_t_rx_clk_delay() != t_rx_clk_delay) { return -5; } + if(hyperbus_get_t_tx_clk_delay() != t_tx_clk_delay) { return -6; } + if(hyperbus_get_address_mask_msb() != address_mask_msb) { return -7; } + if(hyperbus_get_address_space() != address_space) { return -8; } + if(hyperbus_get_phys_in_use() != phys_in_use) { return -9; } + if(hyperbus_get_which_phy() != which_phy) { return -10; } + if(hyperbus_get_t_csh_cycle() != t_csh_cycle) { return -11; } + + int32_t error_code = -1; + + // -- TEST 32b -- + + hyperram_buffer_32[0] = WORD0; + hyperram_buffer_32[1] = WORD1; + + volatile int32_t value_32 = hyperram_buffer_32[0]; + if (value_32 != WORD0) return error_code; + error_code--; + + value_32 = hyperram_buffer_32[1]; + if (value_32 != WORD1) return error_code; + error_code--; + + asm volatile("one: nop"); + //trying longer latency + address_space = 1 & (0x1 << HYPERBUS_ADDRESS_SPACE_ADDRESS_SPACE_BIT); + hyperbus_set_address_space(address_space); + + asm volatile("two: nop"); + uint32_t config_reg = hyperram_buffer_32_config_reg; + //bits 7-4 should be 6 by default for initial latency value (t_latency_access) + //(https://www.infineon.com/dgdl/Infineon-S27KL0641_S27KS0641_S70KL1281_S70KS1281_3.0_V_1.8_V_64_Mb_(8_MB)_128_Mb_(16_MB)_HyperRAM_Self-Refresh_DRAM-DataSheet-v15_00-EN.pdf?fileId=8ac78c8c7d0d8da4017d0ed18c684db5) + + uint16_t initial_latency = (config_reg >> 4) & 0x3; + uint8_t BurstDelay; + + //from Model + if (initial_latency == 0) + BurstDelay = 5; + else if (initial_latency == 1) + BurstDelay = 6; + else if (initial_latency == 0xF) + BurstDelay = 4; + else if (initial_latency == 0xE) + BurstDelay = 3; + + error_code--; + + if (BurstDelay != 6) return error_code; + + BurstDelay = 3; + initial_latency = 0xE; + + config_reg = config_reg & ~(0x7 << 4); + config_reg = config_reg | (initial_latency << 4); + + //writing to config registers has 0 latency + hyperbus_set_t_latency_access(0); + asm volatile("three: nop"); + hyperram_buffer_32_config_reg = config_reg; + + //set it back to 3 now to align periph and flash + hyperbus_set_t_latency_access(3); + config_reg = hyperram_buffer_32_config_reg; + + asm volatile("four: nop"); + uint16_t second_latency = (config_reg >> 4) & 0x3; + + if (second_latency == 0) + BurstDelay = 5; + else if (second_latency == 1) + BurstDelay = 6; + else if (second_latency == 0xF) + BurstDelay = 4; + else if (second_latency == 0xE) + BurstDelay = 3; + + error_code--; + + if (BurstDelay != 3) return error_code; + + //set back address space to 0 + address_space = 0 & (0x1 << HYPERBUS_ADDRESS_SPACE_ADDRESS_SPACE_BIT); + hyperbus_set_address_space(address_space); + + hyperram_buffer_32[2] = WORD2; + hyperram_buffer_32[3] = WORD3; + + error_code--; + + value_32 = hyperram_buffer_32[2]; + if (value_32 != WORD2) return error_code; + + error_code--; + + value_32 = hyperram_buffer_32[3]; + if (value_32 != WORD3) return error_code; + + +#ifdef TEST_SORT_ARRAY_32 + feed_array_32(hyperram_buffer_32, 64); + + bubbleSort_32(hyperram_buffer_32, 64); + + error_code--; + + if(isSorted_32(hyperram_buffer_32, 64) != 0) return error_code; + +#endif + + return 0; + +} \ No newline at end of file diff --git a/sw/device/lib/drivers/hyperbus/hyperbus.c b/sw/device/lib/drivers/hyperbus/hyperbus.c new file mode 100644 index 000000000..f5ed8005c --- /dev/null +++ b/sw/device/lib/drivers/hyperbus/hyperbus.c @@ -0,0 +1,181 @@ +/* + ******************* +******************************* C SOURCE FILE ***************************** +** ******************* +** +** project : X-HEEP +** filename : hyperbus.c +** +*************************************************************************** +** +** Copyright (c) EPFL contributors. +** All rights reserved. +** +*************************************************************************** +*/ + +/***************************************************************************/ +/***************************************************************************/ +/** +* @file hyperbus.c +* @date 09/10/2024 +* @author Davide Schiavone +* @version 1 +* @brief HyperBus driver +*/ +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +/****************************************************************************/ +/** **/ +/* MODULES USED */ +/** **/ +/****************************************************************************/ + +#include "hyperbus.h" +#include "hyperbus_regs.h" // Generated. +#include "hyperbus_structs.h" +#include "bitfield.h" + +void hyperbus_set_t_latency_access(uint8_t t_latency_access) +{ + hyperbus_peri->T_LATENCY_ACCESS = bitfield_field32_write(hyperbus_peri->T_LATENCY_ACCESS, HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_FIELD, t_latency_access); + return; +} + +uint8_t hyperbus_get_t_latency_access() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->T_LATENCY_ACCESS, HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_FIELD); + return (uint8_t)val; +} + +void hyperbus_set_en_latency_additional(uint8_t en_latency_additional) +{ + hyperbus_peri->EN_LATENCY_ADDITIONAL = bitfield_bit32_write(hyperbus_peri->EN_LATENCY_ADDITIONAL, HYPERBUS_EN_LATENCY_ADDITIONAL_EN_LATENCY_ADDITIONAL_BIT, en_latency_additional); + return; +} + +uint8_t hyperbus_get_en_latency_additional() +{ + uint32_t val = bitfield_bit32_read(hyperbus_peri->EN_LATENCY_ADDITIONAL, HYPERBUS_EN_LATENCY_ADDITIONAL_EN_LATENCY_ADDITIONAL_BIT); + return (uint8_t)val; +} + +void hyperbus_set_t_burst_max(uint16_t t_burst_max) +{ + hyperbus_peri->T_BURST_MAX = bitfield_field32_write(hyperbus_peri->T_BURST_MAX, HYPERBUS_T_BURST_MAX_T_BURST_MAX_FIELD, t_burst_max); + return; +} + +uint16_t hyperbus_get_t_burst_max() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->T_BURST_MAX, HYPERBUS_T_BURST_MAX_T_BURST_MAX_FIELD); + return (uint16_t)val; +} + +void hyperbus_set_t_read_write_recovery(uint8_t t_read_write_recovery) +{ + hyperbus_peri->T_READ_WRITE_RECOVERY = bitfield_field32_write(hyperbus_peri->T_READ_WRITE_RECOVERY, HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_FIELD, t_read_write_recovery); + return; +} + +uint8_t hyperbus_get_t_read_write_recovery() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->T_READ_WRITE_RECOVERY, HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_FIELD); + return (uint8_t)val; +} + +void hyperbus_set_t_rx_clk_delay(uint8_t t_rx_clk_delay) +{ + hyperbus_peri->T_RX_CLK_DELAY = bitfield_field32_write(hyperbus_peri->T_RX_CLK_DELAY, HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_FIELD, t_rx_clk_delay); + return; +} + +uint8_t hyperbus_get_t_rx_clk_delay() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->T_RX_CLK_DELAY, HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_FIELD); + return (uint8_t)val; +} + +void hyperbus_set_t_tx_clk_delay(uint8_t t_tx_clk_delay) +{ + hyperbus_peri->T_TX_CLK_DELAY = bitfield_field32_write(hyperbus_peri->T_TX_CLK_DELAY, HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_FIELD, t_tx_clk_delay); + return; +} + +uint8_t hyperbus_get_t_tx_clk_delay() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->T_TX_CLK_DELAY, HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_FIELD); + return (uint8_t)val; +} + +void hyperbus_set_address_mask_msb(uint8_t address_mask_msb) +{ + hyperbus_peri->ADDRESS_MASK_MSB = bitfield_field32_write(hyperbus_peri->ADDRESS_MASK_MSB, HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_FIELD, address_mask_msb); + return; +} + +uint8_t hyperbus_get_address_mask_msb() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->ADDRESS_MASK_MSB, HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_FIELD); + return (uint8_t)val; +} + +void hyperbus_set_address_space(uint8_t address_space) +{ + hyperbus_peri->ADDRESS_SPACE = bitfield_bit32_write(hyperbus_peri->ADDRESS_SPACE, HYPERBUS_ADDRESS_SPACE_ADDRESS_SPACE_BIT, address_space); + return; +} + +uint8_t hyperbus_get_address_space() +{ + uint32_t val = bitfield_bit32_read(hyperbus_peri->ADDRESS_SPACE, HYPERBUS_ADDRESS_SPACE_ADDRESS_SPACE_BIT); + return (uint8_t)val; +} + +void hyperbus_set_phys_in_use(uint8_t phys_in_use) +{ + hyperbus_peri->PHYS_IN_USE = bitfield_bit32_write(hyperbus_peri->PHYS_IN_USE, HYPERBUS_PHYS_IN_USE_PHYS_IN_USE_BIT, phys_in_use); + return; +} + +uint8_t hyperbus_get_phys_in_use() +{ + uint32_t val = bitfield_bit32_read(hyperbus_peri->PHYS_IN_USE, HYPERBUS_PHYS_IN_USE_PHYS_IN_USE_BIT); + return (uint8_t)val; +} + + +void hyperbus_set_which_phy(uint8_t which_phy) +{ + hyperbus_peri->WHICH_PHY = bitfield_bit32_write(hyperbus_peri->WHICH_PHY, HYPERBUS_WHICH_PHY_WHICH_PHY_BIT, which_phy); + return; +} + +uint8_t hyperbus_get_which_phy() +{ + uint32_t val = bitfield_bit32_read(hyperbus_peri->WHICH_PHY, HYPERBUS_WHICH_PHY_WHICH_PHY_BIT); + return (uint8_t)val; +} + +void hyperbus_set_t_csh_cycle(uint8_t t_csh_cycle) +{ + hyperbus_peri->T_CSH_CYCLE = bitfield_field32_write(hyperbus_peri->T_CSH_CYCLE, HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_FIELD, t_csh_cycle); + return; +} + +uint8_t hyperbus_get_t_csh_cycle() +{ + uint32_t val = bitfield_field32_read(hyperbus_peri->T_CSH_CYCLE, HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_FIELD); + return (uint8_t)val; +} + +#ifdef __cplusplus +} +#endif // __cplusplus + +/****************************************************************************/ +/** **/ +/* EOF */ +/** **/ +/****************************************************************************/ \ No newline at end of file diff --git a/sw/device/lib/drivers/hyperbus/hyperbus.h b/sw/device/lib/drivers/hyperbus/hyperbus.h new file mode 100644 index 000000000..01cd398e5 --- /dev/null +++ b/sw/device/lib/drivers/hyperbus/hyperbus.h @@ -0,0 +1,68 @@ +/* + ******************* +******************************* H SOURCE FILE ******************************* +** ******************* ** +** ** +** project : X-HEEP ** +** filename : hyperbus.h ** +** ** +***************************************************************************** +** +** Copyright (c) EPFL contributors. +** All rights reserved. +** +***************************************************************************** +*/ + +/***************************************************************************/ +/***************************************************************************/ +/** +* @file hyperbus.h +* @date 09/10/2024 +* @author Davide Schiavone +* @version 1 +* @brief HyperBus driver +*/ + +#ifndef HYPERBUS_H_ +#define HYPERBUS_H_ + +/****************************************************************************/ +/** **/ +/** MODULES USED **/ +/** **/ +/****************************************************************************/ + +#include +#include +#include + +void hyperbus_set_t_latency_access(uint8_t t_latency_access); +uint8_t hyperbus_get_t_latency_access(); +void hyperbus_set_en_latency_additional(uint8_t en_latency_additional); +uint8_t hyperbus_get_en_latency_additional(); +void hyperbus_set_t_burst_max(uint16_t t_burst_max); +uint16_t hyperbus_get_t_burst_max(); +void hyperbus_set_t_read_write_recovery(uint8_t t_read_write_recovery); +uint8_t hyperbus_get_t_read_write_recovery(); +void hyperbus_set_t_rx_clk_delay(uint8_t t_rx_clk_delay); +uint8_t hyperbus_get_t_rx_clk_delay(); +void hyperbus_set_t_tx_clk_delay(uint8_t t_tx_clk_delay); +uint8_t hyperbus_get_t_tx_clk_delay(); +void hyperbus_set_address_mask_msb(uint8_t address_mask_msb); +uint8_t hyperbus_get_address_mask_msb(); +void hyperbus_set_address_space(uint8_t address_space); +uint8_t hyperbus_get_address_space(); +void hyperbus_set_phys_in_use(uint8_t phys_in_use); +uint8_t hyperbus_get_phys_in_use(); +void hyperbus_set_which_phy(uint8_t which_phy); +uint8_t hyperbus_get_which_phy(); +void hyperbus_set_t_csh_cycle(uint8_t t_csh_cycle); +uint8_t hyperbus_get_t_csh_cycle(); + +#endif // HYPERBUS_H_ +/****************************************************************************/ +/** **/ +/** EOF **/ +/** **/ +/****************************************************************************/ \ No newline at end of file diff --git a/sw/device/lib/drivers/hyperbus/hyperbus_regs.h b/sw/device/lib/drivers/hyperbus/hyperbus_regs.h new file mode 100644 index 000000000..dc3df4f94 --- /dev/null +++ b/sw/device/lib/drivers/hyperbus/hyperbus_regs.h @@ -0,0 +1,88 @@ +// Generated register defines for hyperbus + +// Copyright information found in source file: +// Copyright EPFL contributors. + +// Licensing information found in source file: +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef _HYPERBUS_REG_DEFS_ +#define _HYPERBUS_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif +// Register width +#define HYPERBUS_PARAM_REG_WIDTH 32 + +// T_LATENCY_ACCESS register +#define HYPERBUS_T_LATENCY_ACCESS_REG_OFFSET 0x0 +#define HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_MASK 0xf +#define HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_OFFSET 0 +#define HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_MASK, .index = HYPERBUS_T_LATENCY_ACCESS_T_LATENCY_ACCESS_OFFSET }) + +// EN_LATENCY_ADDITIONAL register +#define HYPERBUS_EN_LATENCY_ADDITIONAL_REG_OFFSET 0x4 +#define HYPERBUS_EN_LATENCY_ADDITIONAL_EN_LATENCY_ADDITIONAL_BIT 0 + +// T_BURST_MAX register +#define HYPERBUS_T_BURST_MAX_REG_OFFSET 0x8 +#define HYPERBUS_T_BURST_MAX_T_BURST_MAX_MASK 0xffff +#define HYPERBUS_T_BURST_MAX_T_BURST_MAX_OFFSET 0 +#define HYPERBUS_T_BURST_MAX_T_BURST_MAX_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_T_BURST_MAX_T_BURST_MAX_MASK, .index = HYPERBUS_T_BURST_MAX_T_BURST_MAX_OFFSET }) + +// T_READ_WRITE_RECOVERY register +#define HYPERBUS_T_READ_WRITE_RECOVERY_REG_OFFSET 0xc +#define HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_MASK 0xf +#define HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_OFFSET 0 +#define HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_MASK, .index = HYPERBUS_T_READ_WRITE_RECOVERY_T_READ_WRITE_RECOVERY_OFFSET }) + +// T_RX_CLK_DELAY register +#define HYPERBUS_T_RX_CLK_DELAY_REG_OFFSET 0x10 +#define HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_MASK 0xf +#define HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_OFFSET 0 +#define HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_MASK, .index = HYPERBUS_T_RX_CLK_DELAY_T_RX_CLK_DELAY_OFFSET }) + +// T_TX_CLK_DELAY register +#define HYPERBUS_T_TX_CLK_DELAY_REG_OFFSET 0x14 +#define HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_MASK 0xf +#define HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_OFFSET 0 +#define HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_MASK, .index = HYPERBUS_T_TX_CLK_DELAY_T_TX_CLK_DELAY_OFFSET }) + +// ADDRESS_MASK_MSB register +#define HYPERBUS_ADDRESS_MASK_MSB_REG_OFFSET 0x18 +#define HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_MASK 0x1f +#define HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_OFFSET 0 +#define HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_MASK, .index = HYPERBUS_ADDRESS_MASK_MSB_ADDRESS_MASK_MSB_OFFSET }) + +// ADDRESS_SPACE register +#define HYPERBUS_ADDRESS_SPACE_REG_OFFSET 0x1c +#define HYPERBUS_ADDRESS_SPACE_ADDRESS_SPACE_BIT 0 + +// PHYS_IN_USE register +#define HYPERBUS_PHYS_IN_USE_REG_OFFSET 0x20 +#define HYPERBUS_PHYS_IN_USE_PHYS_IN_USE_BIT 0 + +// WHICH_PHY register +#define HYPERBUS_WHICH_PHY_REG_OFFSET 0x24 +#define HYPERBUS_WHICH_PHY_WHICH_PHY_BIT 0 + +// T_CSH_CYCLE register +#define HYPERBUS_T_CSH_CYCLE_REG_OFFSET 0x28 +#define HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_MASK 0xf +#define HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_OFFSET 0 +#define HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_FIELD \ + ((bitfield_field32_t) { .mask = HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_MASK, .index = HYPERBUS_T_CSH_CYCLE_T_CSH_CYCLE_OFFSET }) + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _HYPERBUS_REG_DEFS_ +// End generated register defines for hyperbus \ No newline at end of file diff --git a/sw/device/lib/drivers/hyperbus/hyperbus_regs_gen.sh b/sw/device/lib/drivers/hyperbus/hyperbus_regs_gen.sh new file mode 100755 index 000000000..962bf9f43 --- /dev/null +++ b/sw/device/lib/drivers/hyperbus/hyperbus_regs_gen.sh @@ -0,0 +1,6 @@ +# Copyright EPFL contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +echo "Generating SW" +${PYTHON} ../../../../../hw/vendor/pulp_platform_register_interface/vendor/lowrisc_opentitan/util/regtool.py --cdefines -o hyperbus_regs.h ../../../../../hw/vendor/pulp_platform_hyperbus/hyperbus_regs.hjson diff --git a/sw/device/lib/runtime/core_v_mini_mcu.h.tpl b/sw/device/lib/runtime/core_v_mini_mcu.h.tpl index 710cf02e6..dcb3bb8a9 100644 --- a/sw/device/lib/runtime/core_v_mini_mcu.h.tpl +++ b/sw/device/lib/runtime/core_v_mini_mcu.h.tpl @@ -68,6 +68,10 @@ extern "C" { #define FLASH_MEM_SIZE 0x${flash_mem_size_address} #define FLASH_MEM_END_ADDRESS (FLASH_MEM_START_ADDRESS + FLASH_MEM_SIZE) +#define HYPERRAM_MEM_START_ADDRESS 0x${hyperram_mem_start_address} +#define HYPERRAM_MEM_SIZE 0x${hyperram_mem_size_address} +#define HYPERRAM_MEM_END_ADDRESS (HYPERRAM_MEM_START_ADDRESS + HYPERRAM_MEM_SIZE) + #define QTY_INTR ${len(interrupts)} % for key, value in interrupts.items(): #define ${key.upper()} ${value} @@ -81,6 +85,9 @@ extern "C" { #define GPIO_AO_DOMAIN_LIMIT 8 +// HyperRam +#define HYPERRAMNUMCHIPS 1 +#define HYPERRAMNUMPHY 1 #ifdef __cplusplus } // extern "C" diff --git a/sw/linker/link.ld.tpl b/sw/linker/link.ld.tpl index b499b5763..c63a8ba4f 100644 --- a/sw/linker/link.ld.tpl +++ b/sw/linker/link.ld.tpl @@ -20,9 +20,13 @@ MEMORY /* Our testbench is a bit weird in that we initialize the RAM (thus allowing initialized sections to be placed there). Infact we dump all sections to ram. */ - % for i, section in enumerate(xheep.iter_linker_sections()): +% for i, section in enumerate(xheep.iter_linker_sections()): ram${i} (rwxai) : ORIGIN = ${f"{section.start:#08x}"}, LENGTH = ${f"{section.size:#08x}"} % endfor +% if hyperram_is_included in ("yes"): + HYPERRAM (rwx) : ORIGIN = ${f"{int(hyperram_mem_start_address,16):#08x}"}, LENGTH = ${f"{int(hyperram_mem_size_address,16):#08x}"} +% endif + } /* @@ -303,6 +307,16 @@ SECTIONS % endif % endfor + +% if hyperram_is_included in ("yes"): + .hyperram : ALIGN(4) + { + . = ALIGN(4); + *(.xheep_data_hyperram) + . = ALIGN(4); + } >HYPERRAM +% endif + /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } diff --git a/sw/linker/link_flash_load.ld.tpl b/sw/linker/link_flash_load.ld.tpl index 9e7b637f7..258b7f8b9 100644 --- a/sw/linker/link_flash_load.ld.tpl +++ b/sw/linker/link_flash_load.ld.tpl @@ -14,6 +14,9 @@ MEMORY <%flash_end = section.end%> % endfor FLASH_left (rx) : ORIGIN = ${f"{flash_end + int(flash_mem_start_address,16):#08x}"}, LENGTH = ${f"{int(flash_mem_size_address,16) - flash_end:#08x}"} +% if hyperram_is_included in ("yes"): + HYPERRAM (rwx) : ORIGIN = ${f"{int(hyperram_mem_start_address,16):#08x}"}, LENGTH = ${f"{int(hyperram_mem_size_address,16):#08x}"} +% endif } @@ -183,4 +186,14 @@ SECTIONS { . = ALIGN(4); } >FLASH_left + +% if hyperram_is_included in ("yes"): + .hyperram : ALIGN(4) + { + . = ALIGN(4); + *(.xheep_data_hyperram) + . = ALIGN(4); + } >HYPERRAM +% endif + } diff --git a/tb/ext_xbar.sv b/tb/ext_xbar.sv index 32d96b884..4bac3ab24 100644 --- a/tb/ext_xbar.sv +++ b/tb/ext_xbar.sv @@ -135,8 +135,8 @@ module ext_xbar #( // 1-to-N crossbar xbar_varlat_one_to_n #( - .XBAR_NSLAVE (XBAR_NSLAVE), - .AGGREGATE_GNT (32'd0) // the neck request is aggregating all the input masters + .XBAR_NSLAVE(XBAR_NSLAVE), + .AGGREGATE_GNT(32'd0) // the neck request is aggregating all the input masters ) i_xbar_slave ( .clk_i (clk_i), .rst_ni (rst_ni), diff --git a/tb/testharness.sv b/tb/testharness.sv index 9008beb4d..27ed68b9a 100644 --- a/tb/testharness.sv +++ b/tb/testharness.sv @@ -211,10 +211,6 @@ module testharness #( .gpio_11_io(gpio[11]), .gpio_12_io(gpio[12]), .gpio_13_io(gpio[13]), - .gpio_14_io(gpio[14]), - .gpio_15_io(gpio[15]), - .gpio_16_io(gpio[16]), - .gpio_17_io(gpio[17]), .spi_flash_sck_io(spi_flash_sck), .spi_flash_cs_0_io(spi_flash_csb[0]), .spi_flash_cs_1_io(spi_flash_csb[1]), @@ -229,20 +225,24 @@ module testharness #( .spi_sd_1_io(spi_sd_io[1]), .spi_sd_2_io(spi_sd_io[2]), .spi_sd_3_io(spi_sd_io[3]), - .pdm2pcm_pdm_io(gpio[18]), - .pdm2pcm_clk_io(gpio[19]), - .i2s_sck_io(gpio[20]), - .i2s_ws_io(gpio[21]), - .i2s_sd_io(gpio[22]), - .spi2_cs_0_io(gpio[23]), - .spi2_cs_1_io(gpio[24]), - .spi2_sck_io(gpio[25]), - .spi2_sd_0_io(gpio[26]), - .spi2_sd_1_io(gpio[27]), - .spi2_sd_2_io(gpio[28]), - .spi2_sd_3_io(gpio[29]), - .i2c_scl_io(gpio[31]), - .i2c_sda_io(gpio[30]), + .i2s_sck_io(gpio[27]), + .i2s_ws_io(gpio[28]), + .i2s_sd_io(gpio[29]), + .i2c_scl_io(gpio[30]), + .i2c_sda_io(gpio[31]), + .hyper_cs_nio(gpio[14]), + .hyper_ck_io(gpio[15]), + .hyper_ckn_io(gpio[16]), + .hyper_rwds_io(gpio[17]), + .hyper_reset_nio(gpio[18]), + .hyper_dq_0_io(gpio[19]), + .hyper_dq_1_io(gpio[20]), + .hyper_dq_2_io(gpio[21]), + .hyper_dq_3_io(gpio[22]), + .hyper_dq_4_io(gpio[23]), + .hyper_dq_5_io(gpio[24]), + .hyper_dq_6_io(gpio[25]), + .hyper_dq_7_io(gpio[26]), .exit_value_o, .intr_vector_ext_i(intr_vector_ext), .xif_compressed_if(ext_if), @@ -575,9 +575,9 @@ module testharness #( // I2s "microphone"/rx example i2s_microphone i2s_microphone_i ( .rst_ni(rst_ni), - .i2s_sck_i(gpio[20]), - .i2s_ws_i(gpio[21]), - .i2s_sd_o(gpio[22]) + .i2s_sck_i(gpio[27]), + .i2s_ws_i(gpio[28]), + .i2s_sd_o(gpio[29]) ); `ifndef VERILATOR diff --git a/util/mcu_gen.py b/util/mcu_gen.py index 9756b3fdf..07a93112d 100755 --- a/util/mcu_gen.py +++ b/util/mcu_gen.py @@ -530,6 +530,10 @@ def len_extracted_peripherals(peripherals): flash_mem_start_address = string2int(obj['flash_mem']['address']) flash_mem_size_address = string2int(obj['flash_mem']['length']) + hyperram_mem_start_address = string2int(obj['hyperram_mem']['address']) + hyperram_mem_size_address = string2int(obj['hyperram_mem']['length']) + hyperram_is_included = obj['hyperram_mem']['is_included'] + stack_size = string2int(obj['linker_script']['stack_size']) heap_size = string2int(obj['linker_script']['heap_size']) @@ -892,6 +896,9 @@ def len_extracted_peripherals(peripherals): "ext_slave_size_address" : ext_slave_size_address, "flash_mem_start_address" : flash_mem_start_address, "flash_mem_size_address" : flash_mem_size_address, + "hyperram_mem_start_address" : hyperram_mem_start_address, + "hyperram_mem_size_address" : hyperram_mem_size_address, + "hyperram_is_included" : hyperram_is_included, "stack_size" : stack_size, "heap_size" : heap_size, "plic_used_n_interrupts" : plic_used_n_interrupts,