diff --git a/.gitignore b/.gitignore index 350d24bf..76f259dd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ working_dir bender morty todo +!target/sim/verilator/driver.cpp +!target/sim/verilator/idma.sh diff --git a/Bender.yml b/Bender.yml index adc686f1..390dcb79 100644 --- a/Bender.yml +++ b/Bender.yml @@ -124,3 +124,20 @@ sources: - target: idma_test files: - target/rtl/tb_idma_generated.sv + + # Verilator + - target: verilator_test + defines: + PORT_AXI4: ~ + PORT_OBI: ~ + PORT_R_OBI: ~ + PORT_W_AXI4: ~ + BACKEND_NAME: idma_backend_r_obi_w_axi + files: + - test/drivers/axi_driver_slave.sv + - test/drivers/axi_read.sv + - test/drivers/axi_write.sv + - test/drivers/obi_driver_slave.sv + - test/drivers/obi_read.sv + - test/drivers/obi_write.sv + - test/tb_idma_backend.sv diff --git a/idma.mk b/idma.mk index c239168e..fb7f8a9e 100644 --- a/idma.mk +++ b/idma.mk @@ -5,7 +5,7 @@ # Authors: # - Thomas Benz -BENDER ?= bender +BENDER ?= ./bender CAT ?= cat DOT ?= dot GIT ?= git @@ -399,6 +399,10 @@ $(IDMA_VLT_DIR)/%_elab.log: $(IDMA_PICKLE_DIR)/sources.json rm -f $(IDMA_VLT_DIR)/$(IDMA_VLT_TOP).sv.pre cd $(IDMA_VLT_DIR); $(VERILATOR) $(IDMA_VLT_ARGS) $(IDMA_VLT_PARAMS) -Mdir obj_$* $(IDMA_VLT_TOP).sv --top-module $(IDMA_VLT_TOP) 2> $*_elab.log +$(IDMA_VLT_DIR)/idma.f: Bender.yml + mkdir -p $(IDMA_VLT_DIR) + $(BENDER) script verilator -t rtl -t idma_simulation -t snitch_cluster -t verilator -t verilator_test -DSYNTHESIS -DVERILATOR > $@ + idma_verilator_clean: rm -rf $(IDMA_VLT_DIR) @@ -482,6 +486,6 @@ idma_pickle_all: $(IDMA_PICKLE_ALL) idma_hw_all: $(IDMA_FULL_RTL) $(IDMA_INCLUDE_ALL) $(IDMA_FULL_TB) $(IDMA_HJSON_ALL) $(IDMA_WAVE_ALL) -idma_sim_all: $(IDMA_VCS_DIR)/compile.sh $(IDMA_VSIM_DIR)/compile.tcl +idma_sim_all: $(IDMA_VCS_DIR)/compile.sh $(IDMA_VSIM_DIR)/compile.tcl $(IDMA_VLT_DIR)/idma.f idma_all: idma_hw_all idma_sim_all idma_doc_all idma_pickle_all diff --git a/target/.gitignore b/target/.gitignore index dd06202b..e8ac3ac6 100644 --- a/target/.gitignore +++ b/target/.gitignore @@ -1,3 +1,5 @@ doc morty -sim/verilator +sim/verilator/* +!sim/verilator/driver.cpp +!sim/verilator/idma.sh diff --git a/target/sim/verilator/driver.cpp b/target/sim/verilator/driver.cpp new file mode 100644 index 00000000..839be671 --- /dev/null +++ b/target/sim/verilator/driver.cpp @@ -0,0 +1,89 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +#define STR(x) #x +#define EXPAND(x) x +#define STRINGIFY_MACRO(x) STR(x) +#define CONCAT(n1, n2) STRINGIFY_MACRO(EXPAND(n1)EXPAND(n2)) + +#define HDR_NAME_STR CONCAT(VNAME,.h) +#define DPI_HDR_STR CONCAT(VNAME,__Dpi.h) +#define SYMS_HDR_STR CONCAT(VNAME,__Syms.h) + +#include +#include HDR_NAME_STR +#include DPI_HDR_STR +#include SYMS_HDR_STR + +#include +#include +#include +#include +#include + +std::map memory_accesses; +uint32_t curr_access_id = 0xA5A50000; +size_t invalid_writes = 0; + +uint32_t copy_from = 0x1000; +uint32_t copy_to = 0x5000; +uint32_t copy_size = 256; + +std::string vNameStr = STRINGIFY_MACRO(VNAME); + +void idma_read(int addr, int *data, int *delay) { + printf("[DRIVER] Read from %08x: %08x\n", addr, curr_access_id); + *data = curr_access_id; + *delay = 5000; + memory_accesses.insert({addr, curr_access_id}); + curr_access_id++; +} + +void idma_write(int w_addr, int w_data) { + uint32_t orig_addr = w_addr + copy_from - copy_to; + printf("[DRIVER] Write %08x to %08x (original address: %08x)\n", w_data, w_addr, orig_addr); + if (memory_accesses.count(orig_addr) == 0) { + printf("[DRIVER] Write is invalid (never read from there)\n"); + invalid_writes++; + } else if (memory_accesses.at(orig_addr) != w_data) { + printf("[DRIVER] Write is invalid (wrong value)\n"); + invalid_writes++; + } +} + +typedef struct { + unsigned int dst_addr; + unsigned int src_addr; + unsigned int length; +} idma_req_t; + +int main(int argc, char **argv) { + // Verilated::debug(1); + + Verilated::commandArgs(argc, argv); + VNAME *idma = new VNAME(); + Verilated::traceEverOn(true); + svSetScope(svGetScopeFromName(("TOP." + vNameStr.substr(1)).c_str())); + int cycs = 0; + while (!Verilated::gotFinish() && cycs++ < 100000) { + if (cycs == 100) { + printf("Pushing request\n"); + idma->add_request(copy_size, copy_from, copy_to); + } + + idma->eval(); + if (!idma->eventsPending()) break; + Verilated::time(idma->nextTimeSlot()); + } + + idma->final(); + delete idma; + + printf("Testbench terminated. Invalid writes: %ld\n", invalid_writes); + + return 0; +} diff --git a/target/sim/verilator/idma.sh b/target/sim/verilator/idma.sh new file mode 100644 index 00000000..2481ad58 --- /dev/null +++ b/target/sim/verilator/idma.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Copyright 2024 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 + +# Authors: +# - Liam Braun + +verilator -f idma.f --timing --trace --trace-structs --exe --build --structs-packed -j `nproc` \ + -Wno-REDEFMACRO -Wno-UNOPTFLAT -Wno-CASEINCOMPLETE -Wno-MODDUP -Wno-PINMISSING \ + -Wno-WIDTH -Wno-TIMESCALEMOD \ + -Wno-SPLITVAR \ + -CFLAGS "-DVNAME=Vtb_idma_backend" \ + --top tb_idma_backend \ + --cc driver.cpp \ + -o tb_idma diff --git a/test/backend/tb_idma_backend.sv b/test/backend/tb_idma_backend.sv new file mode 100644 index 00000000..75b39595 --- /dev/null +++ b/test/backend/tb_idma_backend.sv @@ -0,0 +1,360 @@ +// Copyright 2024 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 +// - Tobias Senti +// - Liam Braun + +`ifdef PORT_AXI4 +`include "axi/typedef.svh" +`include "../include/axi/assign.svh" +`endif + +`ifdef PORT_OBI +`include "obi/typedef.svh" +`endif + +`include "memory_types.svh" +`include "idma/typedef.svh" + +`ifndef BACKEND_NAME +`define BACKEND_NAME idma_backend_unknown +`endif + +/// Synthesis wrapper for the iDMA backend. Unpacks all the interfaces to simple logic vectors +module tb_idma_backend #( + parameter int unsigned BufferDepth = 3, + parameter int unsigned NumAxInFlight = 3, + parameter int unsigned DataWidth = 32, + parameter int unsigned AddrWidth = 32, + parameter int unsigned UserWidth = 1, + // ID is currently used to differentiate transfers in testbench. We need to fix this + // eventually. + parameter int unsigned AxiIdWidth = 12, + parameter int unsigned TFLenWidth = 32, + parameter int unsigned MemSysDepth = 0, + parameter bit AXI_IdealMemory = 1, + parameter int unsigned AXI_MemNumReqOutst = 1, + parameter int unsigned AXI_MemLatency = 0, + parameter bit OBI_IdealMemory = 1, + parameter int unsigned OBI_MemNumReqOutst = 1, + parameter int unsigned OBI_MemLatency = 0, + parameter bit CombinedShifter = 1'b0, + parameter int unsigned WatchDogNumCycles = 100, + parameter bit MaskInvalidData = 1, + parameter bit RAWCouplingAvail = 0, + parameter bit HardwareLegalizer = 1, + parameter bit RejectZeroTransfers = 1, + parameter bit ErrorHandling = 0, + parameter bit DmaTracing = 1 +)( + output idma_pkg::idma_busy_t idma_busy_o +); + + // timing parameters + localparam time TA = 1ns; + localparam time TT = 9ns; + localparam time TCK = 10ns; + + /// Define the error handling capability + localparam idma_pkg::error_cap_e ErrorCap = ErrorHandling ? idma_pkg::ERROR_HANDLING : + idma_pkg::NO_ERROR_HANDLING; + + // TB parameters + // dependent parameters + localparam int unsigned StrbWidth = DataWidth / 8; + localparam int unsigned OffsetWidth = $clog2(StrbWidth); + typedef logic [AddrWidth-1:0] addr_t; + typedef logic [DataWidth-1:0] data_t; + typedef logic [StrbWidth-1:0] strb_t; + typedef logic [UserWidth-1:0] user_t; + typedef logic [AxiIdWidth-1:0] id_t; + typedef logic [OffsetWidth-1:0] offset_t; + typedef logic [TFLenWidth-1:0] tf_len_t; + + // Clock reset signals + logic clk; + logic rst_n; + + // AXI4+ATOP typedefs + `ifdef PORT_AXI4 +`AXI_TYPEDEF_AW_CHAN_T(axi_aw_chan_t, addr_t, id_t, user_t) +`AXI_TYPEDEF_W_CHAN_T(axi_w_chan_t, data_t, strb_t, user_t) +`AXI_TYPEDEF_B_CHAN_T(axi_b_chan_t, id_t, user_t) + +`AXI_TYPEDEF_AR_CHAN_T(axi_ar_chan_t, addr_t, id_t, user_t) +`AXI_TYPEDEF_R_CHAN_T(axi_r_chan_t, data_t, id_t, user_t) + +`AXI_TYPEDEF_REQ_T(axi_req_t, axi_aw_chan_t, axi_w_chan_t, axi_ar_chan_t) +`AXI_TYPEDEF_RESP_T(axi_rsp_t, axi_b_chan_t, axi_r_chan_t) + `endif + + + // OBI typedefs + `ifdef PORT_OBI +`OBI_TYPEDEF_MINIMAL_A_OPTIONAL(a_optional_t) +`OBI_TYPEDEF_MINIMAL_R_OPTIONAL(r_optional_t) + +`OBI_TYPEDEF_TYPE_A_CHAN_T(obi_a_chan_t, addr_t, data_t, strb_t, id_t, a_optional_t) +`OBI_TYPEDEF_TYPE_R_CHAN_T(obi_r_chan_t, data_t, id_t, r_optional_t) + +`OBI_TYPEDEF_REQ_T(obi_req_t, obi_a_chan_t) +`OBI_TYPEDEF_RSP_T(obi_rsp_t, obi_r_chan_t) + `endif + + + `IDMA_TYPEDEF_FULL_REQ_T(idma_req_t, id_t, addr_t, tf_len_t) + `IDMA_TYPEDEF_FULL_RSP_T(idma_rsp_t, addr_t) + + idma_req_t idma_req [$]; + idma_rsp_t idma_rsp [$]; + + `ifdef PORT_R_AXI4 + typedef struct packed { + axi_ar_chan_t ar_chan; + } axi_read_meta_channel_t; + typedef struct packed { + axi_read_meta_channel_t axi; + } read_meta_channel_t; + + axi_req_t axi_read_req; + axi_rsp_t axi_read_rsp; + `elsif PORT_R_OBI + typedef struct packed { + obi_a_chan_t a_chan; + } obi_read_meta_channel_t; + typedef struct packed { + obi_read_meta_channel_t obi; + } read_meta_channel_t; + + obi_req_t obi_read_req; + obi_rsp_t obi_read_rsp; + `endif + + `ifdef PORT_W_AXI4 + typedef struct packed { + axi_aw_chan_t aw_chan; + } axi_write_meta_channel_t; + typedef struct packed { + axi_write_meta_channel_t axi; + } write_meta_channel_t; + + axi_req_t axi_write_req; + axi_rsp_t axi_write_rsp; + `elsif PORT_W_OBI + typedef struct packed { + obi_a_chan_t a_chan; + } obi_write_meta_channel_t; + typedef struct packed { + obi_write_meta_channel_t obi; + } write_meta_channel_t; + + obi_req_t obi_write_req; + obi_rsp_t obi_write_rsp; + `endif + + idma_req_t curr_idma_req; + idma_rsp_t curr_idma_rsp; + + logic test = 0; + logic req_valid; + logic req_ready; + logic rsp_valid; + logic rsp_ready; + logic eh_req_valid_i; + logic eh_req_ready_o; + idma_pkg::idma_eh_req_t eh_req_i; + + clk_rst_gen #( + .ClkPeriod ( TCK ), + .RstClkCycles ( 1 ) + ) i_clk_rst_gen ( + .clk_o ( clk ), + .rst_no ( rst_n ) + ); + + `BACKEND_NAME #( + .CombinedShifter ( CombinedShifter ), + .DataWidth ( DataWidth ), + .AddrWidth ( AddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .UserWidth ( UserWidth ), + .TFLenWidth ( TFLenWidth ), + .MaskInvalidData ( MaskInvalidData ), + .BufferDepth ( BufferDepth ), + .RAWCouplingAvail ( RAWCouplingAvail ), + .HardwareLegalizer ( HardwareLegalizer ), + .RejectZeroTransfers ( RejectZeroTransfers ), + .ErrorCap ( ErrorCap ), + .NumAxInFlight ( NumAxInFlight ), + .MemSysDepth ( MemSysDepth ), + .idma_req_t ( idma_req_t ), + .idma_rsp_t ( idma_rsp_t ), + .idma_eh_req_t ( idma_pkg::idma_eh_req_t), + .idma_busy_t ( idma_pkg::idma_busy_t ), + `ifdef PORT_AXI4 + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + `endif + `ifdef PORT_OBI + .obi_req_t ( obi_req_t ), + .obi_rsp_t ( obi_rsp_t ), + `endif + .write_meta_channel_t ( write_meta_channel_t ), + .read_meta_channel_t ( read_meta_channel_t ) + ) i_idma_backend ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .testmode_i ( 1'b0 ), + .idma_req_i ( curr_idma_req ), + .req_valid_i ( req_valid ), + .req_ready_o ( req_ready ), + .idma_rsp_o ( curr_idma_rsp ), + .rsp_valid_o ( rsp_valid ), + .rsp_ready_i ( rsp_ready ), + .idma_eh_req_i ( eh_req_i ), + .eh_req_valid_i ( eh_req_valid_i ), + .eh_req_ready_o ( eh_req_ready_o ), + `ifdef PORT_R_AXI4 + .axi_read_req_o ( axi_read_req ), + .axi_read_rsp_i ( axi_read_rsp ), + `elsif PORT_R_OBI + .obi_read_req_o ( obi_read_req ), + .obi_read_rsp_i ( obi_read_rsp ), + `endif + `ifdef PORT_W_AXI4 + .axi_write_req_o ( axi_write_req ), + .axi_write_rsp_i ( axi_write_rsp ), + `elsif PORT_W_OBI + .obi_write_req_o ( obi_write_req ), + .obi_write_rsp_i ( obi_write_rsp ), + `endif + .busy_o ( idma_busy_o ) + ); + + export "DPI-C" function add_request; + idma_req_t new_idma_req; + function add_request(input int length, input int src_addr, input int dst_addr); + new_idma_req = '0; + new_idma_req.length = length; + new_idma_req.src_addr = src_addr; + new_idma_req.dst_addr = dst_addr; + idma_req.push_back(new_idma_req); + endfunction + + always @(posedge clk) begin + if (idma_req.size() > 0) begin + curr_idma_req = '0; + curr_idma_req.dst_addr = idma_req[0].dst_addr; + curr_idma_req.src_addr = idma_req[0].src_addr; + curr_idma_req.length = idma_req[0].length; + `ifdef PORT_R_AXI4 + curr_idma_req.opt.src_protocol = idma_pkg::AXI; + `elsif PORT_R_OBI + curr_idma_req.opt.src_protocol = idma_pkg::OBI; + `endif + `ifdef PORT_W_AXI4 + curr_idma_req.opt.dst_protocol = idma_pkg::AXI; + `elsif PORT_W_OBI + curr_idma_req.opt.dst_protocol = idma_pkg::OBI; + `endif + curr_idma_req.opt.beo.decouple_aw = '0; + curr_idma_req.opt.beo.decouple_rw = '0; + curr_idma_req.opt.beo.src_max_llen = 32; + curr_idma_req.opt.beo.dst_max_llen = 32; + curr_idma_req.opt.beo.src_reduce_len = '1; + curr_idma_req.opt.beo.dst_reduce_len = '1; + + eh_req_i = '0; + eh_req_valid_i = '0; + + req_valid = 1; + + idma_req.pop_back(); + + $display("Sending request"); + while (req_ready != '1) #TA; + #TA req_valid = 0; + $display("Request sent"); + + rsp_ready = '1; + + while (rsp_valid != '1) #TA; + $display("Request complete."); + + $finish; + end + end + + initial begin + #10ns rst_n = '0; + #10ns rst_n = '1; + + $dumpfile("idma_trace.vcd"); + $dumpvars(0); + + #500000ns $display("Terminating, no response received in time."); + $finish; + end + + `ifdef PORT_R_AXI4 + axi_read #( + .axi_req_t(axi_req_t), + .axi_rsp_t(axi_rsp_t), + .DataWidth(DataWidth), + .AddrWidth(AddrWidth), + .UserWidth(UserWidth), + .AxiIdWidth(AxiIdWidth), + .TA(TA), + .TT(TT) + ) i_axi_read ( + .axi_read_req (axi_read_req), + .axi_read_rsp (axi_read_rsp), + .clk_i(clk) + ); + `elsif PORT_R_OBI + obi_read #( + .obi_req_t(obi_req_t), + .obi_rsp_t(obi_rsp_t), + .TA(TA), + .TT(TT) + ) i_obi_read ( + .obi_read_req(obi_read_req), + .obi_read_rsp(obi_read_rsp), + .clk_i(clk), + .rst_ni(rst_n) + ); + `endif + + `ifdef PORT_W_AXI4 + axi_write #( + .axi_req_t(axi_req_t), + .axi_rsp_t(axi_rsp_t), + .DataWidth(DataWidth), + .AddrWidth(AddrWidth), + .UserWidth(UserWidth), + .AxiIdWidth(AxiIdWidth), + .TA(TA), + .TT(TT) + ) i_axi_write ( + .axi_write_req(axi_write_req), + .axi_write_rsp(axi_write_rsp), + .clk_i(clk) + ); + `elsif PORT_W_OBI + obi_write #( + .obi_req_t(obi_req_t), + .obi_rsp_t(obi_rsp_t), + .TA(TA), + .TT(TT) + ) i_obi_write ( + .obi_write_req(obi_write_req), + .obi_write_rsp(obi_write_rsp), + .clk_i(clk), + .rst_ni(rst_n) + ); + `endif +endmodule + diff --git a/test/drivers/axi_driver_common.svh b/test/drivers/axi_driver_common.svh new file mode 100644 index 00000000..add4dc29 --- /dev/null +++ b/test/drivers/axi_driver_common.svh @@ -0,0 +1,67 @@ +// This file contains code from https://github.com/pulp-platform/axi. +// This had to be made separate because Verilator does not support +// all features used in the original file. + +// See copyright notices in +// https://github.com/pulp-platform/axi/blob/master/src/axi_test.sv. + +`ifndef AXI_DRIVER_COMMON_SV +`define AXI_DRIVER_COMMON_SV + +`include "../src/axi_intf.sv" + +/// The data transferred on a beat on the AW/AR channels. +class axi_ax_beat #( +parameter AW = 32, +parameter IW = 8 , +parameter UW = 1 +); +rand logic [IW-1:0] ax_id = '0; +rand logic [AW-1:0] ax_addr = '0; +logic [7:0] ax_len = '0; +logic [2:0] ax_size = '0; +logic [1:0] ax_burst = '0; +logic ax_lock = '0; +logic [3:0] ax_cache = '0; +logic [2:0] ax_prot = '0; +rand logic [3:0] ax_qos = '0; +logic [3:0] ax_region = '0; +logic [5:0] ax_atop = '0; // Only defined on the AW channel. +rand logic [UW-1:0] ax_user = '0; +endclass + +/// The data transferred on a beat on the W channel. +class axi_w_beat #( +parameter DW = 32, +parameter UW = 1 +); +rand logic [DW-1:0] w_data = '0; +rand logic [DW/8-1:0] w_strb = '0; +logic w_last = '0; +rand logic [UW-1:0] w_user = '0; +endclass + +/// The data transferred on a beat on the B channel. +class axi_b_beat #( +parameter IW = 8, +parameter UW = 1 +); +rand logic [IW-1:0] b_id = '0; +axi_pkg::resp_t b_resp = '0; +rand logic [UW-1:0] b_user = '0; +endclass + +/// The data transferred on a beat on the R channel. +class axi_r_beat #( +parameter DW = 32, +parameter IW = 8 , +parameter UW = 1 +); +rand logic [IW-1:0] r_id = '0; +rand logic [DW-1:0] r_data = '0; +axi_pkg::resp_t r_resp = '0; +logic r_last = '0; +rand logic [UW-1:0] r_user = '0; +endclass + +`endif \ No newline at end of file diff --git a/test/drivers/axi_driver_slave.sv b/test/drivers/axi_driver_slave.sv new file mode 100644 index 00000000..21228655 --- /dev/null +++ b/test/drivers/axi_driver_slave.sv @@ -0,0 +1,168 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`include "drivers/axi_driver_common.svh" + +module axi_driver_slave #( + parameter int AW = 32, + parameter int DW = 32, + parameter int IW = 8, + parameter int UW = 1, + parameter time TA = 0ns, // stimuli application time + parameter time TT = 0ns // stimuli test time +)( + input clk_i, + AXI_BUS_DV.Slave sif +); + +typedef axi_ax_beat #(.AW(AW), .IW(IW), .UW(UW)) ax_beat_t; +typedef axi_w_beat #(.DW(DW), .UW(UW)) w_beat_t; +typedef axi_b_beat #(.IW(IW), .UW(UW)) b_beat_t; +typedef axi_r_beat #(.DW(DW), .IW(IW), .UW(UW)) r_beat_t; + +function void reset(); + sif.aw_ready = '0; + sif.w_ready = '0; + sif.b_id = '0; + sif.b_resp = '0; + sif.b_user = '0; + sif.b_valid = '0; + sif.ar_ready = '0; + sif.r_id = '0; + sif.r_data = '0; + sif.r_resp = '0; + sif.r_last = '0; + sif.r_user = '0; + sif.r_valid = '0; +endfunction + +task cycle_start; + #(TT - TA); +endtask + +task cycle_end; + @(posedge clk_i); +endtask + +/// Issue a beat on the B channel. +task send_b ( + input b_beat_t beat +); + cycle_end(); + #TA; + sif.b_id = beat.b_id; + sif.b_resp = beat.b_resp; + sif.b_user = beat.b_user; + sif.b_valid = 1; + cycle_start(); + while (sif.b_ready != 1) begin cycle_end(); cycle_start(); end + cycle_end(); + #TA; + sif.b_id = '0; + sif.b_resp = '0; + sif.b_user = '0; + sif.b_valid = 0; +endtask + +/// Issue a beat on the R channel. +task send_r ( + input r_beat_t beat +); + cycle_end(); + #TA; + sif.r_id = beat.r_id; + sif.r_data = beat.r_data; + sif.r_resp = beat.r_resp; + sif.r_last = beat.r_last; + sif.r_user = beat.r_user; + sif.r_valid = 1; + cycle_start(); + while (sif.r_ready != 1) begin cycle_end(); cycle_start(); end + cycle_end(); + #TA; + sif.r_valid = 0; + sif.r_id = '0; + sif.r_data = '0; + sif.r_resp = '0; + sif.r_last = '0; + sif.r_user = '0; +endtask + +/// Wait for a beat on the AW channel. +task recv_aw ( + output ax_beat_t beat +); + cycle_end(); + #TA; + sif.aw_ready = 1; + cycle_start(); + while (sif.aw_valid != 1) begin cycle_end(); cycle_start(); end + beat = new; + beat.ax_id = sif.aw_id; + beat.ax_addr = sif.aw_addr; + beat.ax_len = sif.aw_len; + beat.ax_size = sif.aw_size; + beat.ax_burst = sif.aw_burst; + beat.ax_lock = sif.aw_lock; + beat.ax_cache = sif.aw_cache; + beat.ax_prot = sif.aw_prot; + beat.ax_qos = sif.aw_qos; + beat.ax_region = sif.aw_region; + beat.ax_atop = sif.aw_atop; + beat.ax_user = sif.aw_user; + cycle_end(); + #TA; + sif.aw_ready = 0; +endtask + +/// Wait for a beat on the W channel. +task recv_w ( + output w_beat_t beat +); + cycle_end(); + #TA; + sif.w_ready = 1; + cycle_start(); + while (sif.w_valid != 1) begin cycle_end(); cycle_start(); end + beat = new; + beat.w_data = sif.w_data; + beat.w_strb = sif.w_strb; + beat.w_last = sif.w_last; + beat.w_user = sif.w_user; + cycle_end(); + #TA; + sif.w_ready = 0; +endtask + +/// Wait for a beat on the AR channel. +task recv_ar ( + output ax_beat_t beat +); + cycle_end(); + #TA; + sif.ar_ready = 1; + cycle_start(); + while (sif.ar_valid != 1) begin cycle_end(); cycle_start(); end + beat = new; + beat.ax_id = sif.ar_id; + beat.ax_addr = sif.ar_addr; + beat.ax_len = sif.ar_len; + beat.ax_size = sif.ar_size; + beat.ax_burst = sif.ar_burst; + beat.ax_lock = sif.ar_lock; + beat.ax_cache = sif.ar_cache; + beat.ax_prot = sif.ar_prot; + beat.ax_qos = sif.ar_qos; + beat.ax_region = sif.ar_region; + beat.ax_atop = 'X; // Not defined on the AR channel. + beat.ax_user = sif.ar_user; + cycle_end(); + #TA; + sif.ar_ready = 0; +endtask + +endmodule diff --git a/test/drivers/axi_read.sv b/test/drivers/axi_read.sv new file mode 100644 index 00000000..04aca30a --- /dev/null +++ b/test/drivers/axi_read.sv @@ -0,0 +1,78 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`include "drivers/axi_driver_common.svh" +`include "drivers/dpi_interfaces.svh" + +module axi_read #( + parameter type axi_req_t, + parameter type axi_rsp_t, + parameter int unsigned DataWidth = 32'd32, + parameter int unsigned AddrWidth = 32'd32, + parameter int unsigned UserWidth = 32'd1, + parameter int unsigned AxiIdWidth = 32'd1, + parameter time TA, + parameter time TT +) ( + input axi_req_t axi_read_req, + output axi_rsp_t axi_read_rsp, + + input logic clk_i +); + +AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AddrWidth), + .AXI_ID_WIDTH(AxiIdWidth), + .AXI_DATA_WIDTH(DataWidth), + .AXI_USER_WIDTH(UserWidth) +) axi_bus (clk_i); + +`AXI_ASSIGN_FROM_REQ ( axi_bus, axi_read_req ); +`AXI_ASSIGN_TO_RESP( axi_read_rsp, axi_bus ); + +axi_driver_slave #( + .AW(AddrWidth), + .DW(DataWidth), + .IW(AxiIdWidth), + .UW(UserWidth), + .TA(TA), + .TT(TT) +) driver ( + .clk_i(clk_i), + .sif(axi_bus) +); + +typedef axi_ax_beat #(.AW(AddrWidth), .IW(AxiIdWidth), .UW(UserWidth)) ax_beat_t; +typedef axi_r_beat #(.DW(DataWidth), .IW(AxiIdWidth), .UW(UserWidth)) r_beat_t; + +task axi_process(); +forever begin + ax_beat_t ar_beat = new; + r_beat_t r_beat = new; + int v; + int delay; + + driver.recv_ar(ar_beat); + $display("[AXI_R] Received AR, %08x", ar_beat.ax_addr); + + fork begin + idma_read(ar_beat.ax_addr, v, delay); + r_beat.r_data = v; + r_beat.r_last = 1; + $display("[AXI_R] Sending R: %08x", r_beat.r_data); + driver.send_r(r_beat); + $display("[AXI_R] Sent R"); + end join +end +endtask + +initial begin + driver.reset(); + axi_process(); +end + +endmodule diff --git a/test/drivers/axi_write.sv b/test/drivers/axi_write.sv new file mode 100644 index 00000000..39d1d2e5 --- /dev/null +++ b/test/drivers/axi_write.sv @@ -0,0 +1,79 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`include "drivers/axi_driver_common.svh" +`include "drivers/dpi_interfaces.svh" + +module axi_write #( + parameter type axi_req_t, + parameter type axi_rsp_t, + parameter int unsigned DataWidth, + parameter int unsigned AddrWidth, + parameter int unsigned UserWidth, + parameter int unsigned AxiIdWidth, + parameter time TA, + parameter time TT +) ( + input axi_req_t axi_write_req, + output axi_rsp_t axi_write_rsp, + + input logic clk_i +); + +AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AddrWidth), + .AXI_ID_WIDTH(AxiIdWidth), + .AXI_DATA_WIDTH(DataWidth), + .AXI_USER_WIDTH(UserWidth) +) axi_bus (clk_i); + +`AXI_ASSIGN_FROM_REQ ( axi_bus, axi_write_req ); +`AXI_ASSIGN_TO_RESP( axi_write_rsp, axi_bus ); + +axi_driver_slave #( + .AW(AddrWidth), + .DW(DataWidth), + .IW(AxiIdWidth), + .UW(UserWidth), + .TA(TA), + .TT(TT) +) driver ( + .clk_i(clk_i), + .sif(axi_bus) +); + +typedef axi_ax_beat #(.AW(AddrWidth), .IW(AxiIdWidth), .UW(UserWidth)) ax_beat_t; +typedef axi_w_beat #(.DW(DataWidth), .UW(UserWidth)) w_beat_t; +typedef axi_b_beat #(.IW(AxiIdWidth), .UW(UserWidth)) b_beat_t; + +task axi_process(); +forever begin + ax_beat_t aw_beat = new; + w_beat_t w_beat = new; + b_beat_t b_beat = new; + + driver.recv_aw(aw_beat); + $display("[AXI_W] Received AW, %08x", aw_beat.ax_addr); + + driver.recv_w(w_beat); + $display("[AXI_W] Received W, %08x", w_beat.w_data); + + idma_write(aw_beat.ax_addr, w_beat.w_data); + + b_beat.b_id = aw_beat.ax_id; + b_beat.b_resp = 0; + driver.send_b(b_beat); + $display("[AXI_W] Sent B"); +end +endtask + +initial begin + driver.reset(); + axi_process(); +end + +endmodule \ No newline at end of file diff --git a/test/drivers/dpi_interfaces.svh b/test/drivers/dpi_interfaces.svh new file mode 100644 index 00000000..5f783d3e --- /dev/null +++ b/test/drivers/dpi_interfaces.svh @@ -0,0 +1,14 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`ifndef DPI_INTERFACE_H +`define DPI_INTERFACE_H + +import "DPI-C" function void idma_read(input int addr, output int data, output int delay); +import "DPI-C" function void idma_write(input int addr, input int data); + +`endif diff --git a/test/drivers/obi_driver_common.svh b/test/drivers/obi_driver_common.svh new file mode 100644 index 00000000..e5e679f3 --- /dev/null +++ b/test/drivers/obi_driver_common.svh @@ -0,0 +1,23 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`ifndef OBI_DRIVER_COMMON_SV +`define OBI_DRIVER_COMMON_SV + +`include "obi/assign.svh" +`include "../src/obi_intf.sv" + +class obi_ar_beat; + logic [31:0] addr; + logic [31:0] wdata; +endclass + +class obi_r_resp; + logic [31:0] data; +endclass + +`endif diff --git a/test/drivers/obi_driver_slave.sv b/test/drivers/obi_driver_slave.sv new file mode 100644 index 00000000..99bc1190 --- /dev/null +++ b/test/drivers/obi_driver_slave.sv @@ -0,0 +1,92 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`include "drivers/obi_driver_common.svh" +`include "../src/obi_pkg.sv" + +module obi_driver_slave #( +parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, +parameter time TA = 0ns, // stimuli application time +parameter time TT = 0ns // stimuli test time +)( + input clk_i, + OBI_BUS_DV.Subordinate sif +); + +function void reset(); +endfunction + +task cycle_start; + #(TT - TA); +endtask + +task cycle_end; + @(posedge clk_i); +endtask + +task recv_r_ar( + output obi_ar_beat addr, +); + cycle_end(); + #TA; + sif.gnt = 1; + cycle_start(); + while (sif.req != '1 || sif.we != 0) begin cycle_end(); cycle_start(); end + addr = new; + addr.addr = sif.addr; + cycle_end(); + #TA; + sif.gnt = 0; +endtask + +task recv_w_ar( + output obi_ar_beat data, +); + cycle_end(); + #TA; + sif.gnt = 1; + cycle_start(); + while (sif.req != '1 || sif.we != 1) begin cycle_end(); cycle_start(); end + data = new; + data.addr = sif.addr; + data.wdata = sif.wdata; + cycle_end(); + #TA; + sif.gnt = 0; +endtask + +task send_r_rsp( + input obi_r_resp data, +); + cycle_end(); + #TA; + sif.rdata = data.data; + sif.rvalid = 1; + cycle_start(); + // if (ObiCfg.UseRReady) begin + while (sif.rready != 1'b1) begin cycle_end(); cycle_start(); end + // end + cycle_end(); + #TA; + sif.rvalid = 0; + sif.rdata = 0; +endtask + +task send_w_rsp(); + cycle_end(); + #TA; + sif.rvalid = 1; + cycle_start(); + // if (ObiCfg.UseRReady) begin + while (sif.rready != 1'b1) begin cycle_end(); cycle_start(); end + // end + cycle_end(); + #TA; + sif.rvalid = 0; +endtask + +endmodule diff --git a/test/drivers/obi_read.sv b/test/drivers/obi_read.sv new file mode 100644 index 00000000..b8c8f40b --- /dev/null +++ b/test/drivers/obi_read.sv @@ -0,0 +1,66 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`include "drivers/obi_driver_common.svh" +`include "drivers/dpi_interfaces.svh" + +module obi_read #( + parameter type obi_req_t, + parameter type obi_rsp_t, + parameter time TA, + parameter time TT +) ( + input obi_req_t obi_read_req, + output obi_rsp_t obi_read_rsp, + + input logic clk_i, + input logic rst_ni +); + +OBI_BUS_DV #( + .OBI_CFG(obi_pkg::ObiDefaultConfig) +) obi_bus (clk_i, rst_ni); + +`OBI_ASSIGN_FROM_REQ( obi_bus, obi_read_req, obi_pkg::ObiDefaultConfig ); +`OBI_ASSIGN_TO_RSP( obi_read_rsp, obi_bus, obi_pkg::ObiDefaultConfig ); + +// TODO: Use a different OBI config to do this automatically +assign obi_bus.rready = obi_read_req.rready; + +obi_driver_slave #( + .TA(TA), + .TT(TT) +) driver ( + .clk_i(clk_i), + .sif(obi_bus) +); + +task obi_process(); +forever begin + obi_ar_beat a = new; + obi_r_resp r_resp = new; + int v; + int delay; + + $display("[OBI] Waiting for read request..."); + driver.recv_r_ar(a); + $display("[OBI] Received A, %08x", a.addr); + + idma_read(a.addr, v, delay); + + r_resp.data = v; + driver.send_r_rsp(r_resp); + $display("[OBI] Sent R"); +end +endtask + +initial begin + driver.reset(); + obi_process(); +end + +endmodule; \ No newline at end of file diff --git a/test/drivers/obi_write.sv b/test/drivers/obi_write.sv new file mode 100644 index 00000000..07c613f3 --- /dev/null +++ b/test/drivers/obi_write.sv @@ -0,0 +1,61 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Liam Braun + +`include "drivers/obi_driver_common.svh" +`include "drivers/dpi_interfaces.svh" + +module obi_write #( + parameter type obi_req_t, + parameter type obi_rsp_t, + parameter time TA, + parameter time TT +) ( + input obi_req_t obi_write_req, + output obi_rsp_t obi_write_rsp, + + input logic clk_i, + input logic rst_ni +); + +OBI_BUS_DV #( + .OBI_CFG(obi_pkg::ObiDefaultConfig) +) obi_bus (clk_i, rst_ni); + +`OBI_ASSIGN_FROM_REQ( obi_bus, obi_write_req, obi_pkg::ObiDefaultConfig ); +`OBI_ASSIGN_TO_RSP( obi_write_rsp, obi_bus, obi_pkg::ObiDefaultConfig ); + +// TODO: Use a different OBI config to do this automatically +assign obi_bus.rready = obi_write_req.rready; + +obi_driver_slave #( + .TA(TA), + .TT(TT) +) driver ( + .clk_i(clk_i), + .sif(obi_bus) +); + +task obi_process(); +forever begin + obi_ar_beat a = new; + + $display("[OBI_W] Waiting for write request..."); + driver.recv_w_ar(a); + $display("[OBI_W] Received write request: %08x to %08x", a.wdata, a.addr); + + idma_write(a.addr, a.wdata); + + driver.send_w_rsp(); +end +endtask + +initial begin + driver.reset(); + obi_process(); +end + +endmodule