Initial commit

* Bare skeleton implementation of everything
* Testbench builds with Verilator
* Test runs
This commit is contained in:
2025-08-23 14:34:23 -07:00
commit 800e9c4008
20 changed files with 1755 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
obj_dir
runs
logs

117
Makefile Normal file
View File

@@ -0,0 +1,117 @@
# Logging control
LOG_REDIR = >&
TIME=/usr/bin/time --format "Elapsed: %E, Memory: %M KB [Swaps %W]"
# Makefile variables
NUM_PROCS=$(shell nproc --all)
PROJ_BASE=$(shell pwd)
# Make and run project
UVM_HOME=$(HOME)/git/uvm-verilator
PROJ=axi
SV_OUT=obj_dir/Vuvm_pkg__verFiles.dat
SV_BUILD_LOG=logs/$(PROJ)_build_sv.log
CPP_OUT=$(PROJ).sim
CPP_BUILD_LOG=logs/$(PROJ)_build_cpp.log
SIM_LOG=$(PROJ)_sim.log
SV_FILES=$(shell ls common/* src/*.sv tb/*.sv)
SV_SRC=$(UVM_HOME)/src/uvm_pkg.sv common/common_pkg.sv src/design_pkg.sv tb/tb_types.sv tb/tb_pkg.sv
DPI_SRC=$(UVM_HOME)/src/dpi/uvm_dpi.cc
DPI_INC=-I/usr/share/verilator/include
SV_DEPS=$(SV_FILES)
CPP_SRC=sim_$(PROJ).cpp
TIMESCALE= --timescale '1ns/1ns'
UVM_DEFINES=+define+UVM_NO_DPI \
+define+UVM_REPORT_DISABLE_FILE_LINE
DISABLED_WARNINGS=-Wno-WIDTHTRUNC -Wno-WIDTHEXPAND \
-Wno-CASTCONST -Wno-CONSTRAINTIGN \
-Wno-MISINDENT -Wno-REALCVT \
-Wno-SYMRSVDWORD -Wno-CASEINCOMPLETE
BUILD_ARGS=-I$(UVM_HOME)/src -I. \
-o $(PROJ).sim \
-j $(NUM_PROCS) \
--error-limit 10 \
--timing $(TIMESCALE) \
--trace \
+define+SVA_ON \
$(UVM_DEFINES) \
$(DISABLED_WARNINGS) \
+incdir+common +incdir+src +incdir+tb \
$(SV_SRC)
ifndef TEST_NAME
TEST_NAME=test_basic
endif
#
# Full build to generate testbench executable (Default target)
#
build: build_cpp
@echo "Build done"
prepare_area:
$(info #----------------)
$(info # Preparing area)
$(info #----------------)
@if [ ! -d $(PROJ_BASE)/logs ]; then mkdir -p $(PROJ_BASE)/logs; fi
#
# C code generation from SystemVerilog
#
build_sv: prepare_area $(SV_OUT)
$(PROJ_BASE)/logs:
@if [ ! -d $(PROJ_BASE)/logs ]; then mkdir -p $(PROJ_BASE)/logs; fi
$(SV_OUT): $(PROJ_BASE)/logs $(SV_DEPS)
$(info #------------)
$(info # Building SV)
$(info #------------)
@verilator --cc $(BUILD_ARGS)
#
# C code build to generate testbench executable
#
build_cpp: build_sv $(CPP_OUT)
$(CPP_OUT): $(SV_OUT)
$(info #-------------)
$(info # Building CPP)
$(info #-------------)
@verilator --binary $(BUILD_ARGS)
#
# Run just lint to detect syntax errors during development
#
lint:
$(info #--------)
$(info # Linting)
$(info #--------)
@verilator --lint-only $(BUILD_ARGS)
#
# Run test. Use TEST_NAME=<test name> on make line to pick the test
#
run: $(CPP_OUT)
$(info #---------------------)
$(info # Running $(TEST_NAME))
$(info #---------------------)
@if [ ! -d runs/$(TEST_NAME) ]; then mkdir -p runs/$(TEST_NAME); fi
@cd runs/$(TEST_NAME) && \
$(PROJ_BASE)/obj_dir/$(PROJ).sim +UVM_TESTNAME=$(TEST_NAME) +UVM_CONFIG_DB_TRACE |& tee $(SIM_LOG)
notify-send "[$(PROJ)] Test run done"
#
# Remove generated files
# - This will not remove 'runs' directory that contains simulation run logs
#
clean:
rm -rf obj_dir logs $(PROJ)*.log

122
Makefile.details Normal file
View File

@@ -0,0 +1,122 @@
# Logging control
ifdef VERBOSE
LOG_REDIR = |tee -a
TIME=stdbuf -o 0 /usr/bin/time --format "Elapsed: %E, Memory: %M KB [Swaps %W]"
else
LOG_REDIR = >&
TIME=/usr/bin/time --format "Elapsed: %E, Memory: %M KB [Swaps %W]"
endif
# Makefile variables
NUM_PROCS=$(shell nproc --all)
PROJ_BASE=$(shell pwd)
# Make and run project
UVM_HOME=$(HOME)/git/uvm-verilator
PROJ=axi
SV_OUT=obj_dir/Vuvm_pkg__verFiles.dat
SV_BUILD_LOG=logs/$(PROJ)_build_sv.log
CPP_OUT=$(PROJ).sim
CPP_BUILD_LOG=logs/$(PROJ)_build_cpp.log
SIM_LOG=$(PROJ)_sim.log
SV_FILES=$(shell ls common/* src/*.sv tb/*.sv)
SV_SRC=$(UVM_HOME)/src/uvm_pkg.sv common/common_pkg.sv src/design_pkg.sv tb/tb_types.sv tb/tb_pkg.sv
DPI_SRC=$(UVM_HOME)/src/dpi/uvm_dpi.cc
DPI_INC=-I/usr/share/verilator/include
SV_DEPS=$(SV_FILES)
CPP_SRC=sim_$(PROJ).cpp
TIMESCALE= --timescale '1ns/1ns'
UVM_DEFINES=+define+UVM_NO_DPI \
+define+UVM_REPORT_DISABLE_FILE_LINE
DISABLED_WARNINGS=-Wno-WIDTHTRUNC -Wno-WIDTHEXPAND \
-Wno-CASTCONST -Wno-CONSTRAINTIGN \
-Wno-MISINDENT -Wno-REALCVT \
-Wno-SYMRSVDWORD -Wno-CASEINCOMPLETE
BUILD_ARGS=-I$(UVM_HOME)/src -I. \
-o $(PROJ).sim \
-j $(NUM_PROCS) \
--error-limit 10 \
--timing $(TIMESCALE) \
--trace \
+define+SVA_ON \
$(UVM_DEFINES) \
$(DISABLED_WARNINGS) \
+incdir+common +incdir+src +incdir+tb \
$(SV_SRC)
ifndef TEST_NAME
TEST_NAME=test_basic
endif
#
# Full build to generate testbench executable (Default target)
#
build: build_cpp
@echo "Build done"
prepare_area:
$(info #----------------)
$(info # Preparing area)
$(info #----------------)
@if [ ! -d $(PROJ_BASE)/logs ]; then mkdir -p $(PROJ_BASE)/logs; fi
#
# C code generation from SystemVerilog
#
build_sv: prepare_area $(SV_OUT)
$(SV_OUT): $(PROJ_BASE)/logs $(SV_DEPS)
$(info #------------)
$(info # Building SV)
$(info #------------)
@$(TIME) verilator --cc $(BUILD_ARGS) $(LOG_REDIR) $(SV_BUILD_LOG) && \
tail -1 $(SV_BUILD_LOG)
#
# C code build to generate testbench executable
#
build_cpp: build_sv $(CPP_OUT)
$(CPP_OUT): $(SV_OUT)
$(info #-------------)
$(info # Building CPP)
$(info #-------------)
@$(TIME) verilator --binary $(BUILD_ARGS) $(LOG_REDIR) $(CPP_BUILD_LOG) && \
tail -1 $(CPP_BUILD_LOG) && \
notify-send "[$(PROJ)] Build done"
#
# Run just lint to detect syntax errors during development
#
lint:
$(info #--------)
$(info # Linting)
$(info #--------)
@$(TIME) verilator --lint-only $(BUILD_ARGS)
#
# Run test. Use TEST_NAME=<test name> on make line to pick the test
#
run: $(CPP_OUT)
$(info #---------------------)
$(info # Running $(TEST_NAME))
$(info #---------------------)
@if [ ! -d runs/$(TEST_NAME) ]; then mkdir -p runs/$(TEST_NAME); fi
@cd runs/$(TEST_NAME) && \
$(TIME) $(PROJ_BASE)/obj_dir/$(PROJ).sim +UVM_TESTNAME=$(TEST_NAME) |& tee $(SIM_LOG)
@notify-send "[$(PROJ)] Test run done"
#
# Remove generated files
# - This will not remove 'runs' directory that contains simulation run logs
#
clean:
rm -rf obj_dir logs $(PROJ)*.log

21
README.md Normal file
View File

@@ -0,0 +1,21 @@
# AXI Protocol Playground
## Introduction
* There is no RTL design here. Just validation components working as both
MANAGER and SUBORDINATE parts
## Dependencies
* Verilator simulator
* UVM that works with Verilator
```
$ cd $UVM_HOME
$ git remote -v
origin https://github.com/antmicro/uvm-verilator.git (fetch)
origin https://github.com/antmicro/uvm-verilator.git (push)
$ git branch
current-patches
* current-patches-2
```

414
common/axi.intf.sv Normal file
View File

@@ -0,0 +1,414 @@
// Generated AXI Interface
// This file is automatically generated by scripts/gen_axi_intf.pl
// Do not edit this file directly.
//
// Interface Name: AXI Signal List
interface axi_intf #(
parameter ADDR_WIDTH = 1,
parameter ARSNOOP_WIDTH = 1,
parameter AWCMO_WIDTH = 1,
parameter AWSNOOP_WIDTH = 1,
parameter BRESP_WIDTH = 1,
parameter CEIL_DATA_WIDTH_DIV_128 = 1,
parameter CEIL_DATA_WIDTH_DIV_128_TMS_4 = 1,
parameter CEIL_DATA_WIDTH_DIV_64 = 1,
parameter DATA_WIDTH = 1,
parameter DATA_WIDTH_DIV_8 = 1,
parameter ID_R_WIDTH = 1,
parameter ID_W_WIDTH = 1,
parameter LOOP_R_WIDTH = 1,
parameter LOOP_W_WIDTH = 1,
parameter MECID_WIDTH = 1,
parameter MPAM_WIDTH = 1,
parameter RCHUNKNUM_WIDTH = 1,
parameter RCHUNKSTRB_WIDTH = 1,
parameter RRESP_WIDTH = 1,
parameter SECSID_WIDTH = 1,
parameter SID_WIDTH = 1,
parameter SSID_WIDTH = 1,
parameter SUBSYSID_WIDTH = 1,
parameter SUM_USER_DATA_WIDTH_USER_RESP_WIDTH = 1,
parameter USER_DATA_WIDTH = 1,
parameter USER_REQ_WIDTH = 1,
parameter USER_RESP_WIDTH = 1
)
(
input ACLK,
input ARESETn
);
// A2.4.1 Clock and reset signals
// External signal: ACLK; // External Global clock signal [External]
// External signal: ARESETn; // External Global reset signal [External]
// A2.1.1 Write request channel
logic AWVALID; // Valid indicator [Manager]
logic AWREADY; // Ready indicator [Subordinate]
logic [ID_W_WIDTH-1:0] AWID; // Transaction identifier for the write channels [Manager]
logic [ADDR_WIDTH-1:0] AWADDR; // Transaction address [Manager]
logic [3:0] AWREGION; // Region identifier [Manager]
logic [7:0] AWLEN; // Transaction length [Manager]
logic [2:0] AWSIZE; // Transaction size [Manager]
logic [1:0] AWBURST; // Burst attribute [Manager]
logic AWLOCK; // Exclusive access indicator [Manager]
logic [3:0] AWCACHE; // Memory attributes [Manager]
logic [2:0] AWPROT; // Access attributes [Manager]
logic AWNSE; // Non-secure extension bit for RME [Manager]
logic [3:0] AWQOS; // QoS identifier [Manager]
logic [USER_REQ_WIDTH-1:0] AWUSER; // User-defined extension to a request [Manager]
logic [1:0] AWDOMAIN; // Shareability domain of a request [Manager]
logic [AWSNOOP_WIDTH-1:0] AWSNOOP; // Write request opcode [Manager]
logic [10:0] AWSTASHNID; // Stash Node ID [Manager]
logic AWSTASHNIDEN; // Stash Node ID enable [Manager]
logic [4:0] AWSTASHLPID; // Stash Logical Processor ID [Manager]
logic AWSTASHLPIDEN; // Stash Logical Processor ID enable [Manager]
logic AWTRACE; // Trace signal [Manager]
logic [LOOP_W_WIDTH-1:0] AWLOOP; // Loopback signals on the write channels [Manager]
logic AWMMUVALID; // MMU signal qualifier [Manager]
logic [SECSID_WIDTH-1:0] AWMMUSECSID; // Secure Stream ID [Manager]
logic [SID_WIDTH-1:0] AWMMUSID; // StreamID [Manager]
logic AWMMUSSIDV; // SubstreamID valid [Manager]
logic [SSID_WIDTH-1:0] AWMMUSSID; // SubstreamID [Manager]
logic AWMMUATST; // Address translated indicator [Manager]
logic [1:0] AWMMUFLOW; // SMMU flow type [Manager]
logic [3:0] AWPBHA; // Page-based Hardware Attributes [Manager]
logic [3:0] AWNSAID; // Non-secure Access ID [Manager]
logic [SUBSYSID_WIDTH-1:0] AWSUBSYSID; // Subsystem ID [Manager]
logic [5:0] AWATOP; // Atomic transaction opcode [Manager]
logic [MPAM_WIDTH-1:0] AWMPAM; // MPAM information with a request [Manager]
logic AWIDUNQ; // Unique ID indicator [Manager]
logic [AWCMO_WIDTH-1:0] AWCMO; // CMO type [Manager]
logic [1:0] AWTAGOP; // Memory Tag operation for write requests [Manager]
logic [MECID_WIDTH-1:0] AWMECID; // Memory Encryption Context identifier [Manager]
// A2.1.1 Write request channel
logic WVALID; // Valid indicator [Manager]
logic WREADY; // Ready indicator [Subordinate]
logic [DATA_WIDTH-1:0] WDATA; // Write data [Manager]
logic [DATA_WIDTH_DIV_8-1:0] WSTRB; // Write data strobes [Manager]
logic [CEIL_DATA_WIDTH_DIV_128_TMS_4-1:0] WTAG; // Memory Tag [Manager]
logic [CEIL_DATA_WIDTH_DIV_128-1:0] WTAGUPDATE; // Memory Tag update [Manager]
logic WLAST; // Last write data [Manager]
logic [USER_DATA_WIDTH-1:0] WUSER; // User-defined extension to write data [Manager]
logic [CEIL_DATA_WIDTH_DIV_64-1:0] WPOISON; // Poison indicator [Manager]
logic WTRACE; // Trace signal [Manager]
// A2.1.3 Write response channel
logic BVALID; // Valid indicator [Subordinate]
logic BREADY; // Ready indicator [Manager]
logic [ID_W_WIDTH-1:0] BID; // Transaction identifier for the write channels [Subordinate]
logic BIDUNQ; // Unique ID indicator [Subordinate]
logic [BRESP_WIDTH-1:0] BRESP; // Write response code [Subordinate]
logic BCOMP; // Completion response indicator [Subordinate]
logic BPERSIST; // Persist response [Subordinate]
logic [1:0] BTAGMATCH; // Memory Tag Match response [Subordinate]
logic [USER_RESP_WIDTH-1:0] BUSER; // User-defined extension to a write response [Subordinate]
logic BTRACE; // Trace signal [Subordinate]
logic [LOOP_W_WIDTH-1:0] BLOOP; // Loopback signals on the write channels [Subordinate]
logic [1:0] BBUSY; // Busy indicator [Subordinate]
// A2.2.1 Read request channel
logic ARVALID; // Valid indicator [Manager]
logic ARREADY; // Ready indicator [Subordinate]
logic [ID_R_WIDTH-1:0] ARID; // Transaction identifier for the read channels [Manager]
logic [ADDR_WIDTH-1:0] ARADDR; // Transaction address [Manager]
logic [3:0] ARREGION; // Region identifier [Manager]
logic [7:0] ARLEN; // Transaction length [Manager]
logic [2:0] ARSIZE; // Transaction size [Manager]
logic [1:0] ARBURST; // Burst attribute [Manager]
logic ARLOCK; // Exclusive access indicator [Manager]
logic [3:0] ARCACHE; // Memory attributes [Manager]
logic [2:0] ARPROT; // Access attributes [Manager]
logic ARNSE; // Non-secure extension bit for RME [Manager]
logic [3:0] ARQOS; // QoS identifier [Manager]
logic [USER_REQ_WIDTH-1:0] ARUSER; // User-defined extension to a request [Manager]
logic [1:0] ARDOMAIN; // Shareability domain of a request [Manager]
logic [ARSNOOP_WIDTH-1:0] ARSNOOP; // Read request opcode [Manager]
logic ARTRACE; // Trace signal [Manager]
logic [LOOP_R_WIDTH-1:0] ARLOOP; // Loopback signals on the read channels [Manager]
logic ARMMUVALID; // MMU signal qualifier [Manager]
logic [SECSID_WIDTH-1:0] ARMMUSECSID; // Secure Stream ID [Manager]
logic [SID_WIDTH-1:0] ARMMUSID; // StreamID [Manager]
logic ARMMUSSIDV; // SubstreamID valid [Manager]
logic [SSID_WIDTH-1:0] ARMMUSSID; // SubstreamID [Manager]
logic ARMMUATST; // Address translated indicator [Manager]
logic [1:0] ARMMUFLOW; // SMMU flow type [Manager]
logic [3:0] ARPBHA; // Page-based Hardware Attributes [Manager]
logic [3:0] ARNSAID; // Non-secure Access ID [Manager]
logic [SUBSYSID_WIDTH-1:0] ARSUBSYSID; // Subsystem ID [Manager]
logic ARCHUNKEN; // Read data chunking enable [Manager]
logic ARIDUNQ; // Unique ID indicator [Manager]
logic [1:0] ARTAGOP; // Memory Tag operation for read requests [Manager]
logic [MECID_WIDTH-1:0] ARMECID; // Memory Encryption Context identifier [Manager]
// A2.2.2 Read data channel
logic RVALID; // Valid indicator [Subordinate]
logic RREADY; // Ready indicator [Manager]
logic [ID_R_WIDTH-1:0] RID; // Transaction identifier for the read channels [Subordinate]
logic RIDUNQ; // Unique ID indicator [Subordinate]
logic [DATA_WIDTH-1:0] RDATA; // Read data [Subordinate]
logic [CEIL_DATA_WIDTH_DIV_128_TMS_4-1:0] RTAG; // Memory Tag [Subordinate]
logic [RRESP_WIDTH-1:0] RRESP; // Read response [Subordinate]
logic RLAST; // Last read data [Subordinate]
logic [SUM_USER_DATA_WIDTH_USER_RESP_WIDTH-1:0] RUSER; // User-defined extension to read data and response [Subordinate]
logic [CEIL_DATA_WIDTH_DIV_64-1:0] RPOISON; // Poison indicator [Subordinate]
logic RTRACE; // Trace signal [Subordinate]
logic [LOOP_R_WIDTH-1:0] RLOOP; // Loopback signals on the read channels [Subordinate]
logic RCHUNKV; // Read data chunking valid [Subordinate]
logic [RCHUNKNUM_WIDTH-1:0] RCHUNKNUM; // Read data chunk number [Subordinate]
logic [RCHUNKSTRB_WIDTH-1:0] RCHUNKSTRB; // Read data chunk strobe [Subordinate]
logic [1:0] RBUSY; // Busy indicator [Subordinate]
// Modport for Manager role
modport MANAGER (
// A2.4.1 Clock and reset signals
input ACLK,
input ARESETn,
// A2.1.1 Write request channel
output AWVALID,
input AWREADY,
output AWID,
output AWADDR,
output AWREGION,
output AWLEN,
output AWSIZE,
output AWBURST,
output AWLOCK,
output AWCACHE,
output AWPROT,
output AWNSE,
output AWQOS,
output AWUSER,
output AWDOMAIN,
output AWSNOOP,
output AWSTASHNID,
output AWSTASHNIDEN,
output AWSTASHLPID,
output AWSTASHLPIDEN,
output AWTRACE,
output AWLOOP,
output AWMMUVALID,
output AWMMUSECSID,
output AWMMUSID,
output AWMMUSSIDV,
output AWMMUSSID,
output AWMMUATST,
output AWMMUFLOW,
output AWPBHA,
output AWNSAID,
output AWSUBSYSID,
output AWATOP,
output AWMPAM,
output AWIDUNQ,
output AWCMO,
output AWTAGOP,
output AWMECID,
// A2.1.1 Write request channel
output WVALID,
input WREADY,
output WDATA,
output WSTRB,
output WTAG,
output WTAGUPDATE,
output WLAST,
output WUSER,
output WPOISON,
output WTRACE,
// A2.1.3 Write response channel
input BVALID,
output BREADY,
input BID,
input BIDUNQ,
input BRESP,
input BCOMP,
input BPERSIST,
input BTAGMATCH,
input BUSER,
input BTRACE,
input BLOOP,
input BBUSY,
// A2.2.1 Read request channel
output ARVALID,
input ARREADY,
output ARID,
output ARADDR,
output ARREGION,
output ARLEN,
output ARSIZE,
output ARBURST,
output ARLOCK,
output ARCACHE,
output ARPROT,
output ARNSE,
output ARQOS,
output ARUSER,
output ARDOMAIN,
output ARSNOOP,
output ARTRACE,
output ARLOOP,
output ARMMUVALID,
output ARMMUSECSID,
output ARMMUSID,
output ARMMUSSIDV,
output ARMMUSSID,
output ARMMUATST,
output ARMMUFLOW,
output ARPBHA,
output ARNSAID,
output ARSUBSYSID,
output ARCHUNKEN,
output ARIDUNQ,
output ARTAGOP,
output ARMECID,
// A2.2.2 Read data channel
input RVALID,
output RREADY,
input RID,
input RIDUNQ,
input RDATA,
input RTAG,
input RRESP,
input RLAST,
input RUSER,
input RPOISON,
input RTRACE,
input RLOOP,
input RCHUNKV,
input RCHUNKNUM,
input RCHUNKSTRB,
input RBUSY
);
// Modport for Subordinate role
modport SUBORDINATE (
// A2.4.1 Clock and reset signals
input ACLK,
input ARESETn,
// A2.1.1 Write request channel
input AWVALID,
output AWREADY,
input AWID,
input AWADDR,
input AWREGION,
input AWLEN,
input AWSIZE,
input AWBURST,
input AWLOCK,
input AWCACHE,
input AWPROT,
input AWNSE,
input AWQOS,
input AWUSER,
input AWDOMAIN,
input AWSNOOP,
input AWSTASHNID,
input AWSTASHNIDEN,
input AWSTASHLPID,
input AWSTASHLPIDEN,
input AWTRACE,
input AWLOOP,
input AWMMUVALID,
input AWMMUSECSID,
input AWMMUSID,
input AWMMUSSIDV,
input AWMMUSSID,
input AWMMUATST,
input AWMMUFLOW,
input AWPBHA,
input AWNSAID,
input AWSUBSYSID,
input AWATOP,
input AWMPAM,
input AWIDUNQ,
input AWCMO,
input AWTAGOP,
input AWMECID,
// A2.1.1 Write request channel
input WVALID,
output WREADY,
input WDATA,
input WSTRB,
input WTAG,
input WTAGUPDATE,
input WLAST,
input WUSER,
input WPOISON,
input WTRACE,
// A2.1.3 Write response channel
output BVALID,
input BREADY,
output BID,
output BIDUNQ,
output BRESP,
output BCOMP,
output BPERSIST,
output BTAGMATCH,
output BUSER,
output BTRACE,
output BLOOP,
output BBUSY,
// A2.2.1 Read request channel
input ARVALID,
output ARREADY,
input ARID,
input ARADDR,
input ARREGION,
input ARLEN,
input ARSIZE,
input ARBURST,
input ARLOCK,
input ARCACHE,
input ARPROT,
input ARNSE,
input ARQOS,
input ARUSER,
input ARDOMAIN,
input ARSNOOP,
input ARTRACE,
input ARLOOP,
input ARMMUVALID,
input ARMMUSECSID,
input ARMMUSID,
input ARMMUSSIDV,
input ARMMUSSID,
input ARMMUATST,
input ARMMUFLOW,
input ARPBHA,
input ARNSAID,
input ARSUBSYSID,
input ARCHUNKEN,
input ARIDUNQ,
input ARTAGOP,
input ARMECID,
// A2.2.2 Read data channel
output RVALID,
input RREADY,
output RID,
output RIDUNQ,
output RDATA,
output RTAG,
output RRESP,
output RLAST,
output RUSER,
output RPOISON,
output RTRACE,
output RLOOP,
output RCHUNKV,
output RCHUNKNUM,
output RCHUNKSTRB,
output RBUSY
);
endinterface // axi_intf

3
common/common_pkg.sv Normal file
View File

@@ -0,0 +1,3 @@
`include "axi.intf.sv"
package common_pkg;
endpackage

336
scripts/gen_axi_intf.pl Executable file
View File

@@ -0,0 +1,336 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my $app = {};
get_axi_intf_data($app);
augment_intf_data($app);
#print Dumper($app->{'intf_data'}); # Debugging output
gen_intf($app);
# -----------------
# Subroutines
# -----------------
sub gen_intf {
my ($app) = @_;
print "// Generated AXI Interface\n";
print "// This file is automatically generated by $0\n";
print "// Do not edit this file directly.\n";
print "//\n";
print "// Interface Name: $app->{'intf_data'}->{'name'}\n";
print "interface axi_intf #(\n";
my @plines = get_param_lines($app);
print join(",\n", @plines);
print "\n)\n";
my @eplines = get_ext_ports_lines($app);
print "\n(\n";
print join(",\n", @eplines);
print "\n);\n";
show_interface($app);
show_modport($app, 'Manager');
show_modport($app, 'Subordinate');
print "endinterface // axi_intf\n";
}
# -----------------
sub get_param_lines {
my ($app) = @_;
my @plines;
foreach my $p (sort { $a cmp $b } keys %{$app->{'intf_data'}->{'params'}}) {
my $param_value = $app->{'intf_data'}->{'params'}->{$p}->{'d'};
push(@plines, sprintf(" parameter %$app->{'intf_data'}->{'info'}->{'param_width'}s = %d",
$p, $param_value));
}
return @plines;
}
# -----------------
sub get_ext_ports_lines {
my ($app) = @_;
my @eplines;
foreach my $p (sort { $a cmp $b } @{$app->{'intf_data'}->{'ext_sigs'}}) {
push(@eplines, sprintf(" input %s",
$p->{'n'}));
}
return @eplines;
}
# -----------------
sub show_interface {
my ($app) = @_;
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
print "\n";
print " // $section->{'name'}\n";
# Display signals
foreach my $signal (@{$section->{'signals'}}) {
if ($signal->{'s'} eq 'External') {
printf " // External signal: %-$app->{'intf_data'}->{'info'}->{'sig_width'}s // $signal->{'d'} [$signal->{'s'}]\n",
"$signal->{'n'};";
} else {
printf " logic %$app->{'intf_data'}->{'info'}->{'dim_width'}s %-$app->{'intf_data'}->{'info'}->{'sig_width'}s // $signal->{'d'} [$signal->{'s'}]\n",
get_dimension($app, $signal->{'w'}), "$signal->{'n'};";
}
}
}
}
# -----------------
sub show_modport{
my ($app, $role) = @_;
my $last = 0;
print "\n";
print " // Modport for $role role\n";
print " modport " . uc($role) . " (\n";
my $num_sections = scalar @{$app->{'intf_data'}->{'sections'}};
my $section_count = 0;
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
print "\n";
print " // $section->{'name'}\n";
$section_count++;
# Display signals
my $num_signals = scalar @{$section->{'signals'}};
my $signal_count = 0;
foreach my $signal (@{$section->{'signals'}}) {
# When role is Manager, signals with 'Manager' source are output, others are input
# When role is Subordinate, signals with 'Subordinate' source are output, others are input
my $direction = ($role eq 'Global') ? 'input' : ($role eq $signal->{'s'}) ? 'output' : 'input';
$signal_count++;
$last = 1 if ($num_signals == $signal_count && $num_sections == $section_count);
printf " %-8s $signal->{'n'}" . (($last == 1) ? "" : ","). "\n", $direction;
}
}
print " );\n";
}
# -----------------
sub augment_intf_data {
my ($app) = @_;
# Pretty print widths
$app->{'intf_data'}->{'info'}->{'param_width'} = 0;
$app->{'intf_data'}->{'info'}->{'dim_width'} = 0;
$app->{'intf_data'}->{'info'}->{'sig_width'} = 0;
# Capture parameters from signals
$app->{'intf_data'}->{'params'} = {};
# Capture external signals
$app->{'intf_data'}->{'ext_sigs'} = [];
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
foreach my $signal (@{$section->{'signals'}}) {
my $width = $signal->{'w'};
if ($width !~ /^\d+$/) {
unless (exists $app->{'intf_data'}->{'params'}->{$width}) {
$app->{'intf_data'}->{'params'}->{$width} = {
'd' => 1, # Default value for the parameter
};
$app->{'intf_data'}->{'info'}->{'param_width'} = length($width)
if (length($width) > $app->{'intf_data'}->{'info'}->{'param_width'});
}
}
if ($signal->{'s'} eq 'External') {
push(@{$app->{'intf_data'}->{'ext_sigs'}}, $signal);
}
}
}
# Find the maximum width of the parameters
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
foreach my $signal (@{$section->{'signals'}}) {
$app->{'intf_data'}->{'info'}->{'dim_width'} = length(get_dimension($app, $signal->{'w'}))
if (length(get_dimension($app, $signal->{'w'})) > $app->{'intf_data'}->{'info'}->{'dim_width'});
$app->{'intf_data'}->{'info'}->{'sig_width'} = length($signal->{'n'})
if (length($signal->{'n'}) > $app->{'intf_data'}->{'info'}->{'sig_width'});
}
$app->{'intf_data'}->{'info'}->{'sig_width'} += 1; # Account for the semi-colon
}
}
# -----------------
sub get_dimension {
my ($app, $width) = @_;
if ($width =~ /^\d+$/) {
if ($width == 1) {
return ""; # Single bit width
} else {
return "[" . ($width-1) . ":0]"; # Specific width
}
} else {
return "[$width-1:0]"; # Width parameter
}
}
# -----------------
sub get_axi_intf_data {
my ($app) = @_;
$app->{'intf_data'} = {
'name' => "AXI Signal List",
'sections' =>
[
{
'name' => 'A2.4.1 Clock and reset signals',
'signals' => [
{ 'n' => 'ACLK', 'w' => '1', 's' => 'External', 'd' => 'External Global clock signal'},
{ 'n' => 'ARESETn', 'w' => '1', 's' => 'External', 'd' => 'External Global reset signal'}
]
},
{
'name' => 'A2.1.1 Write request channel',
'signals' => [
# n: name, w: width, s: source, d: description
{'n' =>'AWVALID','w' =>'1','s' =>'Manager','d' =>'Valid indicator' },
{'n' =>'AWREADY','w' =>'1','s' =>'Subordinate','d' =>'Ready indicator' },
{'n' =>'AWID','w' =>'ID_W_WIDTH','s' =>'Manager','d' =>'Transaction identifier for the write channels' },
{'n' =>'AWADDR','w' =>'ADDR_WIDTH','s' =>'Manager','d' =>'Transaction address' },
{'n' =>'AWREGION','w' =>'4','s' =>'Manager','d' =>'Region identifier' },
{'n' =>'AWLEN','w' =>'8','s' =>'Manager','d' =>'Transaction length' },
{'n' =>'AWSIZE','w' =>'3','s' =>'Manager','d' =>'Transaction size' },
{'n' =>'AWBURST','w' =>'2','s' =>'Manager','d' =>'Burst attribute' },
{'n' =>'AWLOCK','w' =>'1','s' =>'Manager','d' =>'Exclusive access indicator' },
{'n' =>'AWCACHE','w' =>'4','s' =>'Manager','d' =>'Memory attributes' },
{'n' =>'AWPROT','w' =>'3','s' =>'Manager','d' =>'Access attributes' },
{'n' =>'AWNSE','w' =>'1','s' =>'Manager','d' =>'Non-secure extension bit for RME' },
{'n' =>'AWQOS','w' =>'4','s' =>'Manager','d' =>'QoS identifier' },
{'n' =>'AWUSER','w' =>'USER_REQ_WIDTH','s' =>'Manager','d' =>'User-defined extension to a request' },
{'n' =>'AWDOMAIN','w' =>'2','s' =>'Manager','d' =>'Shareability domain of a request' },
{'n' =>'AWSNOOP','w' =>'AWSNOOP_WIDTH','s' =>'Manager','d' =>'Write request opcode' },
{'n' =>'AWSTASHNID','w' =>'11','s' =>'Manager','d' =>'Stash Node ID' },
{'n' =>'AWSTASHNIDEN','w' =>'1','s' =>'Manager','d' =>'Stash Node ID enable' },
{'n' =>'AWSTASHLPID','w' =>'5','s' =>'Manager','d' =>'Stash Logical Processor ID' },
{'n' =>'AWSTASHLPIDEN','w' =>'1','s' =>'Manager','d' =>'Stash Logical Processor ID enable' },
{'n' =>'AWTRACE','w' =>'1','s' =>'Manager','d' =>'Trace signal' },
{'n' =>'AWLOOP','w' =>'LOOP_W_WIDTH','s' =>'Manager','d' =>'Loopback signals on the write channels' },
{'n' =>'AWMMUVALID','w' =>'1','s' =>'Manager','d' =>'MMU signal qualifier' },
{'n' =>'AWMMUSECSID','w' =>'SECSID_WIDTH','s' =>'Manager','d' =>'Secure Stream ID' },
{'n' =>'AWMMUSID','w' =>'SID_WIDTH','s' =>'Manager','d' =>'StreamID' },
{'n' =>'AWMMUSSIDV','w' =>'1','s' =>'Manager','d' =>'SubstreamID valid' },
{'n' =>'AWMMUSSID','w' =>'SSID_WIDTH','s' =>'Manager','d' =>'SubstreamID' },
{'n' =>'AWMMUATST','w' =>'1','s' =>'Manager','d' =>'Address translated indicator' },
{'n' =>'AWMMUFLOW','w' =>'2','s' =>'Manager','d' =>'SMMU flow type' },
{'n' =>'AWPBHA','w' =>'4','s' =>'Manager','d' =>'Page-based Hardware Attributes' },
{'n' =>'AWNSAID','w' =>'4','s' =>'Manager','d' =>'Non-secure Access ID' },
{'n' =>'AWSUBSYSID','w' =>'SUBSYSID_WIDTH','s' =>'Manager','d' =>'Subsystem ID' },
{'n' =>'AWATOP','w' =>'6','s' =>'Manager','d' =>'Atomic transaction opcode' },
{'n' =>'AWMPAM','w' =>'MPAM_WIDTH','s' =>'Manager','d' =>'MPAM information with a request' },
{'n' =>'AWIDUNQ','w' =>'1','s' =>'Manager','d' =>'Unique ID indicator' },
{'n' =>'AWCMO','w' =>'AWCMO_WIDTH','s' =>'Manager','d' =>'CMO type' },
{'n' =>'AWTAGOP','w' =>'2','s' =>'Manager','d' =>'Memory Tag operation for write requests' },
{'n' =>'AWMECID','w' =>'MECID_WIDTH','s' =>'Manager','d' =>'Memory Encryption Context identifier' },
]
},
{
'name' => 'A2.1.1 Write request channel',
'signals' => [
{ 'n' => 'WVALID', 'w' => '1', 's' => 'Manager', 'd' => 'Valid indicator' },
{ 'n' => 'WREADY', 'w' => '1', 's' => 'Subordinate', 'd' => 'Ready indicator' },
{ 'n' => 'WDATA', 'w' => 'DATA_WIDTH', 's' => 'Manager', 'd' => 'Write data' },
{ 'n' => 'WSTRB', 'w' => 'DATA_WIDTH_DIV_8', 's' => 'Manager', 'd' => 'Write data strobes' },
{ 'n' => 'WTAG', 'w' => 'CEIL_DATA_WIDTH_DIV_128_TMS_4', 's' => 'Manager', 'd' => 'Memory Tag' },
{ 'n' => 'WTAGUPDATE', 'w' => 'CEIL_DATA_WIDTH_DIV_128', 's' => 'Manager', 'd' => 'Memory Tag update' },
{ 'n' => 'WLAST', 'w' => '1', 's' => 'Manager', 'd' => 'Last write data' },
{ 'n' => 'WUSER', 'w' => 'USER_DATA_WIDTH', 's' => 'Manager', 'd' => 'User-defined extension to write data' },
{ 'n' => 'WPOISON', 'w' => 'CEIL_DATA_WIDTH_DIV_64', 's' => 'Manager', 'd' => 'Poison indicator' },
{ 'n' => 'WTRACE', 'w' => '1', 's' => 'Manager', 'd' => 'Trace signal' },
]
},
{
'name' => 'A2.1.3 Write response channel',
'signals' => [
{ 'n' => 'BVALID', 'w' => '1', 's' => 'Subordinate', 'd' => 'Valid indicator' },
{ 'n' => 'BREADY', 'w' => '1', 's' => 'Manager', 'd' => 'Ready indicator' },
{ 'n' => 'BID', 'w' => 'ID_W_WIDTH', 's' => 'Subordinate', 'd' => 'Transaction identifier for the write channels' },
{ 'n' => 'BIDUNQ', 'w' => '1', 's' => 'Subordinate', 'd' => 'Unique ID indicator' },
{ 'n' => 'BRESP', 'w' => 'BRESP_WIDTH', 's' => 'Subordinate', 'd' => 'Write response code' },
{ 'n' => 'BCOMP', 'w' => '1', 's' => 'Subordinate', 'd' => 'Completion response indicator' },
{ 'n' => 'BPERSIST', 'w' => '1', 's' => 'Subordinate', 'd' => "Persist response" },
{ 'n' => 'BTAGMATCH', 'w' => '2', 's' => 'Subordinate', 'd' => 'Memory Tag Match response' },
{ 'n' => 'BUSER', 'w' => 'USER_RESP_WIDTH', 's' => 'Subordinate', 'd' => 'User-defined extension to a write response' },
{ 'n' => 'BTRACE', 'w' => '1', 's' => 'Subordinate', 'd' => 'Trace signal' },
{ 'n' => 'BLOOP', 'w' => 'LOOP_W_WIDTH', 's' => 'Subordinate', 'd' => 'Loopback signals on the write channels' },
{ 'n' => 'BBUSY', 'w' => '2', 's' => 'Subordinate', 'd' => "Busy indicator" },
]
},
{
'name' => 'A2.2.1 Read request channel',
'signals' => [
{ 'n' => 'ARVALID', 'w' => '1', 's' => 'Manager', 'd' => 'Valid indicator' },
{ 'n' => 'ARREADY', 'w' => '1', 's' => 'Subordinate', 'd' => 'Ready indicator' },
{ 'n' => 'ARID', 'w' => 'ID_R_WIDTH', 's' => 'Manager', 'd' => 'Transaction identifier for the read channels' },
{ 'n' => 'ARADDR', 'w' => 'ADDR_WIDTH', 's' => 'Manager', 'd' => 'Transaction address' },
{ 'n' => 'ARREGION', 'w' => '4', 's' => 'Manager', 'd' => 'Region identifier' },
{ 'n' => 'ARLEN', 'w' => '8', 's' => 'Manager', 'd'=> "Transaction length" },
{ 'n' => 'ARSIZE', 'w' => '3', 's' => 'Manager', 'd' => 'Transaction size' },
{ 'n' => 'ARBURST', 'w' => '2', 's' => 'Manager', 'd' => 'Burst attribute' },
{ 'n' => 'ARLOCK', 'w' => '1', 's' => 'Manager', 'd' => 'Exclusive access indicator' },
{ 'n' => 'ARCACHE', 'w' => '4', 's' => 'Manager', 'd' => 'Memory attributes' },
{ 'n' => 'ARPROT', 'w' => '3', 's' => 'Manager', 'd' => 'Access attributes' },
{ 'n' => 'ARNSE', 'w' => '1', 's' => 'Manager', 'd' => 'Non-secure extension bit for RME' },
{ 'n' => 'ARQOS', 'w' => '4', 's' => 'Manager', 'd' => 'QoS identifier' },
{ 'n' => 'ARUSER', 'w' => 'USER_REQ_WIDTH', 's' => 'Manager', 'd' => 'User-defined extension to a request' },
{ 'n' => 'ARDOMAIN', 'w' => '2', 's' => 'Manager', 'd' => 'Shareability domain of a request' },
{ 'n' => 'ARSNOOP', 'w' => 'ARSNOOP_WIDTH', 's' => 'Manager', 'd' => 'Read request opcode' },
{ 'n' => 'ARTRACE', 'w' => '1', 's' => 'Manager', 'd' => 'Trace signal' },
{ 'n' => 'ARLOOP', 'w' => 'LOOP_R_WIDTH', 's' => 'Manager', 'd' => 'Loopback signals on the read channels' },
{ 'n' => 'ARMMUVALID', 'w' => '1', 's' => 'Manager', 'd' => 'MMU signal qualifier' },
{ 'n' => 'ARMMUSECSID', 'w' => 'SECSID_WIDTH', 's' => 'Manager', 'd' => 'Secure Stream ID' },
{ 'n' => 'ARMMUSID', 'w' => 'SID_WIDTH', 's' => 'Manager', 'd' => 'StreamID' },
{ 'n' => 'ARMMUSSIDV', 'w' => '1', 's' => 'Manager', 'd' => 'SubstreamID valid' },
{ 'n' => 'ARMMUSSID', 'w' => 'SSID_WIDTH', 's' => 'Manager', 'd' => 'SubstreamID' },
{ 'n' => 'ARMMUATST', 'w' => '1', 's' => 'Manager', 'd' => 'Address translated indicator' },
{ 'n' => 'ARMMUFLOW', 'w' => '2', 's' => 'Manager', 'd' => 'SMMU flow type' },
{ 'n' => 'ARPBHA', 'w' => '4', 's' => 'Manager', 'd' => 'Page-based Hardware Attributes' },
{ 'n' => 'ARNSAID', 'w' => '4', 's' => 'Manager', 'd' => 'Non-secure Access ID' },
{ 'n' => 'ARSUBSYSID', 'w' => 'SUBSYSID_WIDTH', 's' => 'Manager', 'd' => 'Subsystem ID' },
{ 'n' => 'ARCHUNKEN','w' => '1','s' => 'Manager','d' => 'Read data chunking enable'},
{ 'n' => 'ARIDUNQ','w' => '1','s' => 'Manager','d' => 'Unique ID indicator'},
{ 'n' => 'ARTAGOP','w' => '2','s' => 'Manager','d' => 'Memory Tag operation for read requests'},
{ 'n' => 'ARMECID','w' => 'MECID_WIDTH','s' => 'Manager','d' => 'Memory Encryption Context identifier'},
]
},
{
'name' => 'A2.2.2 Read data channel',
'signals' => [
{ 'n' => 'RVALID', 'w' => '1', 's' => 'Subordinate', 'd' => 'Valid indicator' },
{ 'n' => 'RREADY', 'w' => '1', 's' => 'Manager', 'd' => 'Ready indicator' },
{ 'n' => 'RID', 'w' => 'ID_R_WIDTH', 's' => 'Subordinate', 'd' => 'Transaction identifier for the read channels' },
{ 'n' => 'RIDUNQ', 'w' => '1', 's' => 'Subordinate', 'd' => 'Unique ID indicator' },
{ 'n' => 'RDATA', 'w' => 'DATA_WIDTH', 's' => 'Subordinate', 'd' => 'Read data' },
{ 'n' => 'RTAG', 'w' => 'CEIL_DATA_WIDTH_DIV_128_TMS_4', 's' => 'Subordinate', 'd' => 'Memory Tag' },
{ 'n' => 'RRESP', 'w' => 'RRESP_WIDTH', 's' => 'Subordinate', 'd' => 'Read response' },
{ 'n' => 'RLAST', 'w' => '1', 's' => 'Subordinate', 'd' => 'Last read data' },
{ 'n' => 'RUSER', 'w' => 'SUM_USER_DATA_WIDTH_USER_RESP_WIDTH', 's' => 'Subordinate', 'd' => 'User-defined extension to read data and response' },
{ 'n' => 'RPOISON', 'w' => 'CEIL_DATA_WIDTH_DIV_64', 's' => 'Subordinate', 'd' => 'Poison indicator' },
{ 'n' => 'RTRACE', 'w' => '1', 's' => 'Subordinate', 'd' => 'Trace signal' },
{ 'n' => 'RLOOP', 'w' => 'LOOP_R_WIDTH', 's' => 'Subordinate', 'd' => 'Loopback signals on the read channels' },
{ 'n' => 'RCHUNKV','w' => '1', 's' => 'Subordinate', 'd' => 'Read data chunking valid' },
{ 'n' => 'RCHUNKNUM', 'w' => 'RCHUNKNUM_WIDTH', 's' => 'Subordinate', 'd' => 'Read data chunk number' },
{ 'n' => 'RCHUNKSTRB', 'w' => 'RCHUNKSTRB_WIDTH', 's' => 'Subordinate', 'd' => 'Read data chunk strobe' },
{ 'n' => 'RBUSY', 'w' => '2', 's' => 'Subordinate', 'd' => 'Busy indicator' },
]
},
]
}
}

0
src/design_pkg.sv Normal file
View File

88
tb/axi_agent.sv Normal file
View File

@@ -0,0 +1,88 @@
// ----------------------------------------------------------------------
// Agent to operate AXI transactions
// This agent includes a driver, monitor, and sequencer for AXI transactions
// ----------------------------------------------------------------------
typedef axi_sequencer;
class axi_agent extends uvm_agent;
agent_type_t agent_type;
virtual `AXI_INTF.MANAGER m_if;
virtual `AXI_INTF.SUBORDINATE s_if;
// Declare the sequencer and driver
axi_sequencer sequencer;
axi_driver driver;
axi_monitor monitor;
`uvm_component_utils_begin(axi_agent)
`uvm_field_enum(agent_type_t, agent_type, UVM_DEFAULT)
`uvm_component_utils_end
// --------------------------------------------------
// Constructor
function new(string name = "axi_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
// --------------------------------------------------
// Set agent type
function void set_agent_type(agent_type_t atype);
agent_type = atype;
`uvm_info("set_agent_type", $sformatf("Agent type set to %s", agent_type.name()), UVM_LOW)
endfunction
// --------------------------------------------------
// Build phase
virtual function void build_phase(uvm_phase phase);
sequencer = axi_sequencer::type_id::create("sequencer", this);
driver = axi_driver::type_id::create("driver", this);
monitor = axi_monitor::type_id::create("monitor", this);
// Propagete the agent type to driver and monitor
driver.set_agent_type(agent_type);
monitor.set_agent_type(agent_type);
if (agent_type == MANAGER) begin
if (!uvm_config_db#(virtual `AXI_INTF.MANAGER)::get(this, "", "axi_dvr_vif", m_if)) begin
`uvm_fatal("axi_agent", "AXI agent MANAGER interface not configured")
end
`uvm_info("axi_agent", $sformatf("Using AXI agent MANAGER interface: axi_dvr_vif"), UVM_LOW)
end else begin // if (agent_type == SUBORDINATE) begin
if (!uvm_config_db#(virtual `AXI_INTF.SUBORDINATE)::get(this, "", "axi_dvr_vif", s_if)) begin
`uvm_fatal("axi_agent", "AXI agent SUBORDINATE interface not configured")
end
`uvm_info("axi_agent", $sformatf("Using AXI agent SUBORDINATE interface: axi_dvr_vif"), UVM_LOW)
end
driver.set_virtual_intefaces(m_if, s_if);
monitor.set_virtual_intefaces(m_if, s_if);
endfunction
// --------------------------------------------------
// Connect phase
virtual function void connect_phase(uvm_phase phase);
// Connect the driver to the sequencer
driver.seq_item_port.connect(sequencer.seq_item_export);
driver.rsp_port.connect(sequencer.rsp_export);
// Connect the monitor to the sequencer
// driver.rsp_port.connect(monitor.rsp_export);
endfunction
// --------------------------------------------------
// Run phase
virtual function void run_phase(uvm_phase phase);
`uvm_info("axi_agent", $sformatf("Running AXI agent: %s as %s",
get_full_name(), agent_type.name()), UVM_LOW)
endfunction
endclass : axi_agent
// ----------------------------------------------------------------------
class axi_sequencer extends uvm_sequencer;
`uvm_component_utils(axi_sequencer)
// Constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass : axi_sequencer

119
tb/axi_driver.sv Normal file
View File

@@ -0,0 +1,119 @@
// ----------------------------------------------------------------------
// Driver for AXI transactions
// ----------------------------------------------------------------------
class axi_driver extends uvm_driver; // #(axi_transaction);
virtual `AXI_INTF.MANAGER m_if;
virtual `AXI_INTF.SUBORDINATE s_if;
agent_type_t agent_type;
`uvm_component_utils(axi_driver)
// --------------------------------------------------
// Constructor
function new(string name = "axi_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
// --------------------------------------------------
// Set agent type
function void set_agent_type(agent_type_t atype);
agent_type = atype;
`uvm_info("set_agent_type", $sformatf("Agent type set to %s", agent_type.name()), UVM_LOW)
endfunction
// --------------------------------------------------
// Set virtual interfaces
function void set_virtual_intefaces(virtual `AXI_INTF.MANAGER m_if_p,
virtual `AXI_INTF.SUBORDINATE s_if_p);
`uvm_info("set_virtual_intefaces", $sformatf("Setting virtual interfaces"), UVM_LOW)
m_if = m_if_p;
s_if = s_if_p;
// if (m_if == null && s_if == null) begin
// `uvm_fatal("AXI_DRIVER", "Both MANAGER and SUBORDINATE interfaces are null")
// end else if ((agent_type == MANAGER) && (m_if == null)) begin
// `uvm_error("AXI_DRIVER", $sformatf("MANAGER interface is null"))
// end else if ((agent_type == SUBORDINATE) && (s_if == null)) begin
// `uvm_error("AXI_DRIVER", $sformatf("SUBORDINATE interface is null"))
// end
endfunction
// --------------------------------------------------
// Run phase
virtual task run_phase(uvm_phase phase);
`uvm_info("axi_driver", $sformatf("Running AXI driver: %s as %s",
get_full_name(), agent_type.name()), UVM_LOW)
if (agent_type == MANAGER) begin
`uvm_info("axi_driver", $sformatf("Manager agent detected, driving transactions"), UVM_LOW)
drive_transactions();
end else if (agent_type == SUBORDINATE) begin
`uvm_info("axi_driver", $sformatf("Subordinate agent detected, responding to transactions"), UVM_LOW)
respond_to_transactions();
end else begin
`uvm_fatal("AXI_DRIVER", "Unknown agent type")
end
endtask
// ------------------------------------------------------------
task drive_transactions();
if (m_if == null) begin
`uvm_error("AXI_DRIVER", "MANAGER interface is null, cannot drive transactions")
end
forever begin
`uvm_info("axi_driver", $sformatf("Starting to drive transactions for %s (rst_n = %0b)",
get_full_name(), m_if.ARESETn), UVM_LOW)
if (m_if.ARESETn == 0) begin
`uvm_info("axi_driver", $sformatf("Waiting for reset to be released (rst_n = %0b)",
m_if.ARESETn), UVM_LOW)
@(posedge m_if.ARESETn);
end
`uvm_info("axi_driver", $sformatf("Starting to drive transactions for %s (rst_n = %0b)",
get_full_name(), m_if.ARESETn), UVM_LOW)
while (m_if.ARESETn != 0) begin
uvm_sequence_item txn;
`uvm_info("axi_driver", $sformatf("Waiting for next transaction"), UVM_LOW)
seq_item_port.get_next_item(txn);
drive_item(txn);
seq_item_port.item_done();
end
end
endtask
// ------------------------------------------------------------
task drive_item(uvm_sequence_item txn);
axi_transaction req;
if (!$cast(req, txn)) begin
`uvm_fatal("AXI_DRIVER", "Invalid transaction type")
end
`uvm_info("drive_item", $sformatf("Driving AXI transaction:\n%s", req.sprint()), UVM_LOW)
@(posedge m_if.ACLK);
m_if.AWVALID = 1;
m_if.AWADDR = req.addr;
@(posedge m_if.ACLK);
m_if.WDATA = req.data;
m_if.WSTRB = req.strb;
`uvm_info("drive_item", $sformatf("Driving AXI transaction done."), UVM_LOW)
endtask
// ------------------------------------------------------------
task respond_to_transactions();
forever begin
@(posedge s_if.ARESETn);
while (s_if.ARESETn != 0) begin
@(posedge s_if.ACLK);
@(negedge s_if.ACLK);
end
end
endtask
endclass : axi_driver

117
tb/axi_monitor.sv Normal file
View File

@@ -0,0 +1,117 @@
// ----------------------------------------------------------------------
// Monitor for AXI transactions
// ----------------------------------------------------------------------
class axi_monitor extends uvm_monitor; // #(axi_transaction);
virtual `AXI_INTF.MANAGER m_if;
virtual `AXI_INTF.SUBORDINATE s_if;
agent_type_t agent_type;
// Declare the analysis export
uvm_analysis_port ap;
// Filehandle for transaction tracker file
int trk_file;
`uvm_component_utils(axi_monitor)
// --------------------------------------------------
// Constructor
function new(string name = "axi_monitor", uvm_component parent = null);
super.new(name, parent);
ap = new("analysis_export", this);
endfunction
// --------------------------------------------------
// Set agent type
function void set_agent_type(agent_type_t atype);
agent_type = atype;
`uvm_info("set_agent_type", $sformatf("Agent type set to %s", agent_type.name()), UVM_LOW)
endfunction
// --------------------------------------------------
// Set virtual interfaces
function void set_virtual_intefaces(virtual `AXI_INTF.MANAGER m_if_p,
virtual `AXI_INTF.SUBORDINATE s_if_p);
`uvm_info("set_virtual_intefaces", $sformatf("Setting virtual interfaces"), UVM_LOW)
m_if = m_if_p;
s_if = s_if_p;
endfunction
// --------------------------------------------------
// Run phase
virtual function void run_phase(uvm_phase phase);
// Open transaction log file
trk_file = $fopen($sformatf("axi_transactions.%s.log", agent_type.name()), "w");
if (trk_file == 0) begin
`uvm_error("AXI_MONITOR", "Failed to open transaction log file")
end else begin
`uvm_info("AXI_MONITOR", "Transaction log file opened successfully", UVM_LOW)
end
// Start monitoring
do_monitor();
endfunction
// --------------------------------------------------
// Monitor logic to capture transactions
virtual task do_monitor();
if (agent_type == MANAGER) begin
do_manager_monitor();
end else if (agent_type == SUBORDINATE) begin
do_subordinate_monitor();
end else begin
`uvm_fatal("AXI_MONITOR", "Unknown agent type")
end
endtask
// --------------------------------------------------
// Monitor logic for MANAGER agent
virtual task do_manager_monitor();
// Placeholder for actual monitoring logic
// This would typically involve sampling signals and creating transactions
forever begin
axi_transaction txn;
@(posedge m_if.ACLK);
txn = axi_transaction::type_id::create("txn");
// Capture transaction details here
ap.write(txn);
write_transaction_to_file(txn);
end
endtask
// --------------------------------------------------
// Monitor logic for SUBORDINATE agent
virtual task do_subordinate_monitor();
// Placeholder for actual monitoring logic
// This would typically involve sampling signals and creating transactions
forever begin
axi_transaction txn;
@(posedge s_if.ACLK);
txn = axi_transaction::type_id::create("txn");
// Capture transaction details here
ap.write(txn);
write_transaction_to_file(txn);
end
endtask
// --------------------------------------------------
// Report phase
virtual function void report_phase(uvm_phase phase);
// Report any captured transactions or statistics
`uvm_info("axi_monitor", $sformatf("Reporting on %s",
get_full_name()), UVM_LOW)
endfunction
// --------------------------------------------------
// Function to write captured transaction into a file
function void write_transaction_to_file(axi_transaction txn);
$fwrite(trk_file, "%s\n", txn.sprint());
endfunction
endclass : axi_monitor

43
tb/axi_transaction.sv Normal file
View File

@@ -0,0 +1,43 @@
// ----------------------------------------------------------------------
class axi_transaction extends uvm_sequence_item;
// Declare AXI transaction fields
rand axi_transaction_type_t txn_type; // Transaction type (read/write)
rand bit [31:0] addr; // Address
rand bit [31:0] data; // Data
rand bit [3:0] strb; // Byte enable
`uvm_object_utils_begin(axi_transaction)
`uvm_field_enum(axi_transaction_type_t, txn_type, UVM_DEFAULT)
`uvm_field_int(addr, UVM_DEFAULT)
`uvm_field_int(data, UVM_DEFAULT)
`uvm_field_int(strb, UVM_DEFAULT)
`uvm_object_utils_end
// Constructor
function new(string name = "axi_transaction");
super.new(name);
endfunction
// Copy method for cloning
virtual function uvm_object clone();
axi_transaction copy;
copy = axi_transaction::type_id::create(get_name());
copy.txn_type = this.txn_type;
copy.addr = this.addr;
copy.data = this.data;
copy.strb = this.strb;
return copy;
endfunction
// Comparison method for checking equality
virtual function bit compare(uvm_object rhs);
axi_transaction other;
if (!$cast(other, rhs)) return 0; // Ensure type match
return (this.txn_type == other.txn_type) &&
(this.addr == other.addr) &&
(this.data == other.data) &&
(this.strb == other.strb);
endfunction
endclass : axi_transaction

40
tb/tb_env.sv Normal file
View File

@@ -0,0 +1,40 @@
// Testbench environment for UVM-based verification
class tb_env extends uvm_env;
axi_agent axi_m;
axi_agent axi_s;
`uvm_component_utils(tb_env)
// ------------------------------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
// ------------------------------------------------------------
virtual function void build_phase(uvm_phase phase);
axi_m = axi_agent::type_id::create("axi_m", this);
axi_s = axi_agent::type_id::create("axi_s", this);
`uvm_info("tb_env", $sformatf("Building testbench environment: %s", get_full_name()), UVM_LOW)
// Set agent types in AXI agents
axi_m.set_agent_type(MANAGER);
axi_s.set_agent_type(SUBORDINATE);
endfunction
// ------------------------------------------------------------
virtual function void connect_phase(uvm_phase phase);
`uvm_info("tb_env", $sformatf("Connecting testbench environment: %s", get_full_name()), UVM_LOW)
endfunction
// ------------------------------------------------------------
virtual function void report_phase(uvm_phase phase);
`uvm_info("tb_env", $sformatf("Reporting for testbench environment: %s", get_full_name()), UVM_LOW)
// Add any specific report phase tasks here
endfunction
// ------------------------------------------------------------
function uvm_sequencer_base get_axi_m_sequencer();
return axi_m.sequencer;
endfunction
endclass : tb_env

26
tb/tb_intf.sv Normal file
View File

@@ -0,0 +1,26 @@
// Testbench interface for UVM-based verification environment
interface testbench_if (
input clk,
virtual axi_intf #(.ADDR_WIDTH(`ADDR_WIDTH),
.CEIL_DATA_WIDTH_DIV_128(`CEIL_DATA_WIDTH_DIV_128),
.CEIL_DATA_WIDTH_DIV_128_TMS_4(`CEIL_DATA_WIDTH_DIV_128_TMS_4),
.CEIL_DATA_WIDTH_DIV_64(`CEIL_DATA_WIDTH_DIV_64),
.DATA_WIDTH(`DATA_WIDTH),
.DATA_WIDTH_DIV_8(`DATA_WIDTH_DIV_8)) m_if,
virtual axi_intf #(.ADDR_WIDTH(`ADDR_WIDTH),
.CEIL_DATA_WIDTH_DIV_128(`CEIL_DATA_WIDTH_DIV_128),
.CEIL_DATA_WIDTH_DIV_128_TMS_4(`CEIL_DATA_WIDTH_DIV_128_TMS_4),
.CEIL_DATA_WIDTH_DIV_64(`CEIL_DATA_WIDTH_DIV_64),
.DATA_WIDTH(`DATA_WIDTH),
.DATA_WIDTH_DIV_8(`DATA_WIDTH_DIV_8)) s_if);
logic rst_n;
initial begin
forever begin
@(clk or rst_n);
$monitor("@%6t: %b %b ", $time,
rst_n, clk);
end
end
endinterface

39
tb/tb_params.sv Normal file
View File

@@ -0,0 +1,39 @@
// ----------------------------------------------------------------------
// Tesbench defines
// ----------------------------------------------------------------------
// List of AXI parameters used in this testbench
`define ADDR_WIDTH 32
// `define ARSNOOP_WIDTH 1
// `define AWCMO_WIDTH 1
// `define AWSNOOP_WIDTH 1
// `define BRESP_WIDTH 1
`define CEIL_DATA_WIDTH_DIV_128 1
`define CEIL_DATA_WIDTH_DIV_128_TMS_4 4
`define CEIL_DATA_WIDTH_DIV_64 1
`define DATA_WIDTH 64
`define DATA_WIDTH_DIV_8 8
// `define ID_R_WIDTH 1
// `define ID_W_WIDTH 1
// `define LOOP_R_WIDTH 1
// `define LOOP_W_WIDTH 1
// `define MECID_WIDTH 1
// `define MPAM_WIDTH 1
// `define RCHUNKNUM_WIDTH 1
// `define RCHUNKSTRB_WIDTH 1
// `define RRESP_WIDTH 1
// `define SECSID_WIDTH 1
// `define SID_WIDTH 1
// `define SSID_WIDTH 1
// `define SUBSYSID_WIDTH 1
// `define SUM_USER_DATA_WIDTH_USER_RESP_WIDTH 1
// `define USER_DATA_WIDTH 1
// `define USER_REQ_WIDTH 1
// `define USER_RESP_WIDTH 1
// Defines
`define AXI_INTF axi_intf #(.ADDR_WIDTH(`ADDR_WIDTH), \
.CEIL_DATA_WIDTH_DIV_128(`CEIL_DATA_WIDTH_DIV_128), \
.CEIL_DATA_WIDTH_DIV_128_TMS_4(`CEIL_DATA_WIDTH_DIV_128_TMS_4), \
.CEIL_DATA_WIDTH_DIV_64(`CEIL_DATA_WIDTH_DIV_64), \
.DATA_WIDTH(`DATA_WIDTH), \
.DATA_WIDTH_DIV_8(`DATA_WIDTH_DIV_8))

25
tb/tb_pkg.sv Normal file
View File

@@ -0,0 +1,25 @@
`include "tb_params.sv"
`include "tb_intf.sv"
package tb_pkg;
import uvm_pkg::*;
import tb_types::*;
`include "uvm_macros.svh"
// Testbench defines
// UVM data items
`include "axi_transaction.sv"
// UVM components
`include "axi_driver.sv"
`include "axi_monitor.sv"
`include "axi_agent.sv"
`include "tb_env.sv"
// UVM sequences
`include "tb_seq_base.sv"
// Tests
`include "test_base.sv"
endpackage
`include "tb_top.sv"

51
tb/tb_seq_base.sv Normal file
View File

@@ -0,0 +1,51 @@
// Base sequence class for testbench sequences
class tb_seq_base extends uvm_sequence;
`uvm_object_utils(tb_seq_base)
// Constructor
function new(string name = "tb_seq_base");
super.new(name);
endfunction
endclass : tb_seq_base
// ----------------------------------------------------------------------
class axi_m_seq_base extends tb_seq_base;
tb_env env;
`uvm_object_utils(axi_m_seq_base)
// Constructor
function new(string name = "axi_m_seq_base");
super.new(name);
endfunction
// Task to start the sequence
virtual task body();
bit ok = uvm_config_db#(tb_env)::get(uvm_root::get(), "*", "env", env);
endtask
endclass : axi_m_seq_base
// ----------------------------------------------------------------------
class axi_simple_seq extends axi_m_seq_base;
`uvm_object_utils(axi_simple_seq)
// Constructor
function new(string name = "axi_simple_seq");
super.new(name);
endfunction
// Task to start the sequence
virtual task body();
axi_transaction txn;
super.body();
`uvm_info("axi_simple_seq", "Starting simple AXI sequence", UVM_LOW)
`uvm_do(txn, env.get_axi_m_sequencer(), -1, {
addr == 32'h0000_0000; // Example address
data == 32'hDEAD_BEEF; // Example data
strb == 'hf; // Example byte enable
});
endtask
endclass : axi_simple_seq

49
tb/tb_top.sv Normal file
View File

@@ -0,0 +1,49 @@
// Testbench top module for UVM-based verification environment
import uvm_pkg::*;
module tb_top (input logic sys_clk);
logic clk;
logic rst_n;
// AXI interface for manager and subordinate
`AXI_INTF a_if (.ACLK(clk), .ARESETn(rst_n));
// // Instantiate the UVM testbench interface
// testbench_if tb_if (
// .clk(clk),
// .m_if(m_if),
// .s_if(s_if)
// );
// assign tb_if.rst_n = rst_n;
// --------------------------------------------------
initial begin
uvm_config_db#(virtual `AXI_INTF.MANAGER)::set(uvm_root::get(), "uvm_test_top.env.axi_m", "axi_dvr_vif", a_if.MANAGER);
uvm_config_db#(virtual `AXI_INTF.SUBORDINATE)::set(uvm_root::get(), "uvm_test_top.env.axi_s", "axi_dvr_vif", a_if.SUBORDINATE);
run_test();
end
// --------------------------------------------------
initial begin
$dumpfile("wave.vcd");
$dumpvars();
end
// --------------------------------------------------
// Clock generation
// TODO: Move to interface. Parameterize frequency
initial begin
clk = 0; // Initialize clock to 0 at time 0
forever begin
#5ns clk = ~clk; // Toggle clock every 5 ns
end
end
// --------------------------------------------------
initial begin
rst_n = 0;
#20ns rst_n = 1; // Release reset after 20 ns
end
endmodule

13
tb/tb_types.sv Normal file
View File

@@ -0,0 +1,13 @@
// Types
package tb_types;
// Agent type
typedef enum {
MANAGER,
SUBORDINATE
} agent_type_t;
typedef enum {
AXI_READ,
AXI_WRITE
} axi_transaction_type_t;
endpackage

129
tb/test_base.sv Normal file
View File

@@ -0,0 +1,129 @@
// Base class for testbench tests
class test_base extends uvm_test;
`uvm_component_utils(test_base)
tb_env env;
uvm_table_printer tb_printer;
// ------------------------------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
// ------------------------------------------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = tb_env::type_id::create("env", this);
tb_printer = new("tb_printer");
uvm_config_db#(tb_env)::set(uvm_root::get(), "*", "env", env);
endfunction
// ------------------------------------------------------------
virtual function void end_of_elaboration_phase(uvm_phase phase);
`uvm_info("end_of_elaboration_phase", $sformatf("Testbench topology:\n%s", this.sprint(tb_printer)), UVM_LOW)
display_uvm_config_db();
display_uvm_config_db("uvm_test_top.env.axi_m");
display_uvm_config_db("uvm_test_top.env.axi_s");
dump_uvm_config_db();
endfunction
// ------------------------------------------------------------
virtual task run_phase(uvm_phase phase);
uvm_objection objection;
objection = phase.get_objection();
`uvm_info("run_phase", $sformatf("Raising objection"), UVM_LOW)
phase.raise_objection(this);
run_reset_phase(phase);
run_test_phase(phase);
run_flush_phase(phase);
objection.set_drain_time(this, 20);
`uvm_info("run_phase", $sformatf("Dropping objection"), UVM_LOW)
phase.drop_objection(this);
endtask
// ------------------------------------------------------------
virtual task run_reset_phase(uvm_phase phase);
`uvm_info("run_reset_phase", $sformatf("Starting reset"), UVM_LOW)
`uvm_info("run_reset_phase", $sformatf("Finishing reset"), UVM_LOW)
endtask
// ------------------------------------------------------------
virtual task run_test_phase(uvm_phase phase);
`uvm_warning("run_test_phase", $sformatf("This content is expected to be implemented in specific tests"))
endtask
// ------------------------------------------------------------
virtual task run_flush_phase(uvm_phase phase);
`uvm_info("run_flush_phase", $sformatf("Finishing test"), UVM_LOW)
endtask
// ------------------------------------------------------------
// Function to traverse and display all entries in uvm_config_db
function void display_uvm_config_db(string scope = ".*");
uvm_resource_pool rp = uvm_resource_pool::get();
uvm_resource_types::rsrc_q_t resources;
// Get all resources from the resource pool
resources = rp.lookup_regex(scope, get_full_name());
rp.print_resources(resources, 1);
`uvm_info("CONFIG_DB", "Traversing uvm_config_db contents:", UVM_LOW)
if (resources.size() == 0) begin
`uvm_info("CONFIG_DB", "No entries found in uvm_config_db", UVM_LOW)
return;
end
// Iterate through all resources
do begin
uvm_resource_base r = resources.pop_front();
if (r == null) begin
`uvm_info("CONFIG_DB", "No more resources to process", UVM_LOW)
break;
end
`uvm_info("CONFIG_DB", $sformatf("Resource: %s", r.get_name()), UVM_LOW)
end while (1);
endfunction
// ------------------------------------------------------------
// Function to dump all entries in uvm_config_db
function void dump_uvm_config_db();
uvm_resource_pool rp = uvm_resource_pool::get();
endfunction
endclass
// ----------------------------------------------------------------------
class test_basic extends test_base;
`uvm_component_utils(test_basic)
// ------------------------------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
// ------------------------------------------------------------
virtual task run_test_phase(uvm_phase phase);
axi_simple_seq seq;
`uvm_info("run_test_phase", $sformatf("Starting stimulus"), UVM_LOW)
#100ns;
seq = axi_simple_seq::type_id::create("seq");
if (seq == null) begin
`uvm_fatal("run_test_phase", "Failed to create sequence instance")
end
seq.start(env.get_axi_m_sequencer());
`uvm_info("run_test_phase", $sformatf("Finishing stimulus"), UVM_LOW)
endtask
endclass