From 5491713c763d15fe96c00bb51c7382b36ec27409 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 19:57:04 +0300 Subject: [PATCH 1/9] Add AHB wrapper and controller --- EF_SRAM_1024X32.yaml | 6 +- hdl/bus_wrapper/SRAM_1024x32_ahb_wrapper.v | 112 +++++++++ hdl/bus_wrapper/SRAM_1024x32_wb_wrapper.v | 4 +- hdl/controllers/ram_ahb_controller.v | 232 ++++++++++++++++++ .../ram_wb_controller.v | 4 +- 5 files changed, 352 insertions(+), 6 deletions(-) create mode 100644 hdl/bus_wrapper/SRAM_1024x32_ahb_wrapper.v create mode 100644 hdl/controllers/ram_ahb_controller.v rename hdl/{bus_wrapper => controllers}/ram_wb_controller.v (96%) diff --git a/EF_SRAM_1024X32.yaml b/EF_SRAM_1024X32.yaml index edebab8..9475560 100644 --- a/EF_SRAM_1024X32.yaml +++ b/EF_SRAM_1024X32.yaml @@ -14,11 +14,13 @@ info: - SRAM - memory bus: - - generic + - WB + - AHB type: hard status: verified width: 387.87 height: 303.315 technology: sky130 digital_supply_voltage: 1.8 - analog_supply_voltage: n/a \ No newline at end of file + analog_supply_voltage: n/a +registers: [] diff --git a/hdl/bus_wrapper/SRAM_1024x32_ahb_wrapper.v b/hdl/bus_wrapper/SRAM_1024x32_ahb_wrapper.v new file mode 100644 index 0000000..5374a14 --- /dev/null +++ b/hdl/bus_wrapper/SRAM_1024x32_ahb_wrapper.v @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2024 Efabless Corporation and its Licensors, All Rights Reserved +// ======================================================================================== +// +// This software is protected by copyright and other intellectual property +// rights. Therefore, reproduction, modification, translation, compilation, or +// representation of this software in any manner other than expressly permitted +// is strictly prohibited. +// +// You may access and use this software, solely as provided, solely for the purpose of +// integrating into semiconductor chip designs that you create as a part of the +// of Efabless shuttles or Efabless managed production programs (and solely for use and +// fabrication as a part of Efabless production purposes and for no other purpose. You +// may not modify or convey the software for any other purpose. +// +// Disclaimer: EFABLESS AND ITS LICENSORS MAKE NO WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, WITH REGARD TO THIS MATERIAL, AND EXPRESSLY DISCLAIM +// ANY AND ALL WARRANTIES OF ANY KIND INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE. Efabless reserves the right to make changes without further +// notice to the materials described herein. Neither Efabless nor any of its licensors +// assume any liability arising out of the application or use of any product or +// circuit described herein. Efabless's products described herein are +// not authorized for use as components in life-support devices. +// +// If you have a separate agreement with Efabless pertaining to the use of this software +// then that agreement shall control. + +`ifdef USE_POWER_PINS + `define USE_PG_PIN +`endif + +module SRAM_1024x32_ahb_wrapper #(parameter AW = 12) ( +`ifdef USE_POWER_PINS + inout VPWR, + inout VGND, +`endif + // AHB Slave ports + input HCLK, + input HRESETn, + + input wire HSEL, + input wire [31:0] HADDR, + input wire [1:0] HTRANS, + input wire HWRITE, + input wire HREADY, + input wire [31:0] HWDATA, + input wire [2:0] HSIZE, + output wire HREADYOUT, + output wire [31:0] HRDATA + +); + +// ram ports +wire [31:0] DO; +wire [31:0] DI; +wire [31:0] BEN; +wire [9:0] AD; +wire EN; +wire R_WB; +wire CLKin; + +ram_ahb_controller #(.AW(AW)) ram_controller( + .HCLK(HCLK), + .HRESETn(HRESETn), + .HSEL(HSEL), + .HADDR(HADDR), + .HTRANS(HTRANS), + .HSIZE(HSIZE), + .HWDATA(HWDATA), + .HWRITE(HWRITE), + .HREADY(HREADY), + .HREADYOUT(HREADYOUT), + .HRDATA(HRDATA), + .DO(DO), + .DI(DI), + .BEN(BEN), + .AD(AD), + .EN(EN), + .R_WB(R_WB) +); + +EF_SRAM_1024x32 SRAM_0 ( +`ifdef USE_POWER_PINS + .vgnd(VGND), + .vnb(VGND), + .vpb(VPWR), + .vpwra(VPWR), + .vpwrm(), + .vpwrp(VPWR), +`endif + .vpwrac(1'b1), + .vpwrpc(1'b1), + // access ports + .DO(DO), + .DI(DI), + .BEN(BEN), + .AD(AD), + .EN(EN), + .R_WB(R_WB), + .CLKin(HCLK), + // scan ports + .TM(1'b0), + .SM(1'b0), + .ScanInCC(1'b0), + .ScanInDL(1'b0), + .ScanInDR(1'b0), + .ScanOutCC(), + .WLBI(1'b0), + .WLOFF(1'b0) +); + +endmodule \ No newline at end of file diff --git a/hdl/bus_wrapper/SRAM_1024x32_wb_wrapper.v b/hdl/bus_wrapper/SRAM_1024x32_wb_wrapper.v index 778c53a..5302f8a 100644 --- a/hdl/bus_wrapper/SRAM_1024x32_wb_wrapper.v +++ b/hdl/bus_wrapper/SRAM_1024x32_wb_wrapper.v @@ -29,7 +29,7 @@ `define USE_PG_PIN `endif -module SRAM_1024x32_wb_wrapper ( +module SRAM_1024x32_wb_wrapper #(parameter AW = 12) ( `ifdef USE_POWER_PINS inout VPWR, inout VGND, @@ -57,7 +57,7 @@ wire EN; wire R_WB; wire CLKin; -ram_wb_controller #(.AW(10)) ram_controller( +ram_wb_controller #(.AW(AW)) ram_controller( .wb_clk_i(wb_clk_i), .wb_rst_i(wb_rst_i), .wbs_stb_i(wbs_stb_i), diff --git a/hdl/controllers/ram_ahb_controller.v b/hdl/controllers/ram_ahb_controller.v new file mode 100644 index 0000000..888a789 --- /dev/null +++ b/hdl/controllers/ram_ahb_controller.v @@ -0,0 +1,232 @@ +/* + Copyright 2023 AUC Open H/W Lab + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +`timescale 1ns/1ps +`default_nettype none + +module ram_ahb_controller #( parameter AW = 13) // Address width + ( + input wire HCLK, + input wire HRESETn, + + input wire HSEL, + input wire [31:0] HADDR, + input wire [1:0] HTRANS, + input wire HWRITE, + input wire HREADY, + input wire [31:0] HWDATA, + input wire [2:0] HSIZE, + output reg HREADYOUT, + output wire [31:0] HRDATA, + + // RAM ports + input wire [31:0] DO, + output wire [31:0] DI, + output wire [31:0] BEN, + output wire [AW-3:0] AD, + output wire EN, + output wire R_WB +); +/////////////////////////////// + wire [31:0] SRAMRDATA; + wire [3:0] SRAMWEN; + wire [31:0] SRAMWDATA; + wire SRAMCS; + wire [AW-3:0] SRAMADDR; + wire SRAMCLK; + + // RAM connections + assign SRAMRDATA = DO; + assign DI = SRAMWDATA; + assign BEN = {{8{SRAMWEN[3]}}, {8{SRAMWEN[2]}}, {8{SRAMWEN[1]}}, {8{SRAMWEN[0]}}}; + assign AD = SRAMADDR; + assign EN = SRAMCS; + assign R_WB = ~(SRAMWEN[3] | SRAMWEN[2] | SRAMWEN[1] | SRAMWEN[0]); +/////////////////////////// + + reg [(AW-3):0] buf_addr; // Write address buffer + reg [ 3:0] buf_we; // Write enable buffer (data phase) + reg buf_hit; // High when reading a wrote-pending data + reg [31:0] buf_data; // AHB write bus buffered + reg buf_pend; // Buffer write data valid + reg buf_data_en; // Data buffer write enable (data phase) + + wire ahb_wr = HTRANS[1] & HSEL & HREADY & HWRITE; + wire ahb_rd = HTRANS[1] & HSEL & HREADY & ~HWRITE; + + /* + SRAM read involves address and data phases which matches that of an AHB + transactions. However, SRAM write requires both data and address to be present + concurrently. This causes an issue for a read operation after a write operation. + The solution is to delay the write to be done after the read and provide a mean + to short circuit the data if you read from the location to be written. + */ + + // Stored write data in pending state if new transfer is read + // buf_data_en indicate new write (data phase) + // ahb_rd indicate new read (address phase) + // buf_pend is registered version of buf_pend_nxt + wire buf_pend_nxt = (buf_pend | buf_data_en) & ahb_rd; + + always @(posedge HCLK or negedge HRESETn) + if (~HRESETn) + buf_pend <= 1'b0; + else + buf_pend <= buf_pend_nxt; + + // RAM write happens when + // - write pending (buf_pend), or + // - new AHB write seen (buf_data_en) at data phase, + // - and not reading (address phase) + reg ram_write_stall; + wire ram_write = ((buf_pend | buf_data_en) & (~ahb_rd)) | ram_write_stall; + // RAM WE is the buffered WE + assign SRAMWEN = {4{ram_write}} & buf_we[3:0]; + + // RAM address is the buffered address for RAM write otherwise HADDR + assign SRAMADDR = ahb_rd ? HADDR[AW-1:2] : buf_addr; + + // RAM chip select during read or write + assign SRAMCS = ahb_rd | ram_write ; + + assign SRAMCLK = HCLK; + + // ---------------------------------------------------------- + // Byte lane decoder and next state logic + // ---------------------------------------------------------- + wire is_byte = (HSIZE == 3'b000); + wire is_half = (HSIZE == 3'b001); + wire is_word = (HSIZE == 3'b010); + + wire byte_0 = is_byte & (HADDR[1:0] == 2'b00); + wire byte_1 = is_byte & (HADDR[1:0] == 2'b01); + wire byte_2 = is_byte & (HADDR[1:0] == 2'b10); + wire byte_3 = is_byte & (HADDR[1:0] == 2'b11); + + wire half_0 = is_half & ~HADDR[1]; + wire half_2 = is_half & HADDR[1]; + + wire byte_we_0 = is_word | half_0 | byte_0; + wire byte_we_1 = is_word | half_0 | byte_1; + wire byte_we_2 = is_word | half_2 | byte_2; + wire byte_we_3 = is_word | half_2 | byte_3; + + // Address phase byte lane strobe + wire [3:0] buf_we_nxt = {4{ahb_wr}} & {byte_we_3, byte_we_2, byte_we_1, byte_we_0}; + + // buf_we keep the valid status of each byte (data phase) + always @(posedge HCLK or negedge HRESETn) + if (~HRESETn) + buf_we <= 4'b0000; + else if(ahb_wr) + buf_we <= buf_we_nxt; + + // buf_data_en is data phase write control + always @(posedge HCLK or negedge HRESETn) + if (~HRESETn) + buf_data_en <= 1'b0; + else + buf_data_en <= ahb_wr & ~HADDR[23]; // bit 23 is exclusivily used for writing to delay_cycles register + + always @(posedge HCLK) begin + if(buf_data_en) begin + if(buf_we[3]) buf_data[31:24] <= HWDATA[31:24]; + if(buf_we[2]) buf_data[23:16] <= HWDATA[23:16]; + if(buf_we[1]) buf_data[15: 8] <= HWDATA[15: 8]; + if(buf_we[0]) buf_data[ 7: 0] <= HWDATA[ 7: 0]; + end + end + + always @(posedge HCLK or negedge HRESETn) begin + if (~HRESETn) + buf_addr <= {(AW-2){1'b0}}; + else if (ahb_wr) + buf_addr <= HADDR[(AW-1):2]; + end + + // Do we have a matching Address (hit)? + wire buf_hit_nxt = (HADDR[AW-1:2] == buf_addr[AW-3 - 0:0]); + always @(posedge HCLK or negedge HRESETn) + if (~HRESETn) + buf_hit <= 1'b0; + else if(ahb_rd) + buf_hit <= buf_hit_nxt; + + /* + Handle the short circuit scenario, a read from a location + which has a write-pending operation. In this case return + the pending data + */ + wire [ 3:0] short = {4{buf_hit}} & buf_we; + assign HRDATA = { + short[3] ? buf_data[31:24] : SRAMRDATA[31:24], + short[2] ? buf_data[23:16] : SRAMRDATA[23:16], + short[1] ? buf_data[15: 8] : SRAMRDATA[15: 8], + short[0] ? buf_data[ 7: 0] : SRAMRDATA[ 7: 0] + }; + + /* + SRAMWDATA comes from the pending write data if any; otherwise it + comes from HWDATA + */ + assign SRAMWDATA = (buf_pend) ? buf_data : HWDATA[31:0]; + + // Number Delay cycles is delay_cycles when writing, no delay cycles for reading + // default case is no delay cycles + + reg [2:0] delay_cycles; + reg [2:0] delay_count; + reg ahb_wr_stall; + reg last_delay_cycle_wr; + + always@ (posedge HCLK or negedge HRESETn) begin + if (!HRESETn) begin + last_delay_cycle_wr <= 0; + end else if(HREADY) begin + last_delay_cycle_wr <= HADDR[23]==1'b1 && ahb_wr; + end + end + + always @(posedge HCLK or negedge HRESETn) + if (~HRESETn) + delay_cycles <= 3'b000; + else if(last_delay_cycle_wr) + delay_cycles <= 3'b000; // disable delay cycles for now // there is a bug here if reverted happened when HREADYOUT is 0 + + always @(posedge HCLK or negedge HRESETn) begin + if (~HRESETn) begin + HREADYOUT <= 1'b1; + delay_count <= 3'b000; + ahb_wr_stall <= 3'b000; + ram_write_stall <= 1'b0; + end + else if((delay_count < delay_cycles) && (ahb_wr_stall|ahb_wr)) begin + HREADYOUT <= 1'b0; + delay_count <= delay_count + 3'b001; + ahb_wr_stall <= 1'b1; + ram_write_stall <= 1'b1; + end + else begin + HREADYOUT <= 1'b1; + delay_count <= 3'b000; + ahb_wr_stall <= ahb_wr; + ram_write_stall <= 1'b0; + end + end + + +endmodule \ No newline at end of file diff --git a/hdl/bus_wrapper/ram_wb_controller.v b/hdl/controllers/ram_wb_controller.v similarity index 96% rename from hdl/bus_wrapper/ram_wb_controller.v rename to hdl/controllers/ram_wb_controller.v index 0e42506..610a8ad 100644 --- a/hdl/bus_wrapper/ram_wb_controller.v +++ b/hdl/controllers/ram_wb_controller.v @@ -25,7 +25,7 @@ // If you have a separate agreement with Efabless pertaining to the use of this software // then that agreement shall control. -module ram_wb_controller #(parameter AW = 10) ( +module ram_wb_controller #(parameter AW = 12) ( // wishbone input wb_clk_i, input wb_rst_i, @@ -50,7 +50,7 @@ module ram_wb_controller #(parameter AW = 10) ( assign EN = wbs_stb_i & wbs_cyc_i; assign R_WB = ~wbs_we_i; assign CLKin = wb_clk_i; - assign AD = wbs_adr_i[AW-1:0]; + assign AD = wbs_adr_i[AW-1:2]; assign DI = wbs_dat_i; assign BEN = {{8{wbs_sel_i[3]}}, {8{wbs_sel_i[2]}}, {8{wbs_sel_i[1]}}, {8{wbs_sel_i[0]}}}; From 377d9ddbdeb5acfbf77c679355a7f5e8952fefa5 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 19:57:25 +0300 Subject: [PATCH 2/9] Add uvm verification directory --- verify/uvm-python/.gitignore | 13 ++ verify/uvm-python/Makefile | 38 +++++ verify/uvm-python/sram_agent/sram_driver.py | 30 ++++ verify/uvm-python/sram_agent/sram_monitor.py | 32 ++++ .../uvm-python/sram_coverage/sram_coverage.py | 29 ++++ verify/uvm-python/sram_interface/sram_if.py | 10 ++ verify/uvm-python/sram_item/sram_item.py | 31 ++++ verify/uvm-python/sram_logger/sram_logger.py | 23 +++ .../sram_ref_model/sram_ref_model.py | 146 ++++++++++++++++++ .../sram_seq_lib/sram_bus_base_seq.py | 55 +++++++ .../sram_seq_lib/sram_corners_seq.py | 98 ++++++++++++ .../uvm-python/sram_seq_lib/sram_init_seq.py | 34 ++++ verify/uvm-python/sram_seq_lib/sram_ip_seq.py | 30 ++++ .../sram_seq_lib/sram_write_read_seq.py | 56 +++++++ verify/uvm-python/test_lib.py | 130 ++++++++++++++++ verify/uvm-python/top.v | 54 +++++++ 16 files changed, 809 insertions(+) create mode 100644 verify/uvm-python/.gitignore create mode 100644 verify/uvm-python/Makefile create mode 100644 verify/uvm-python/sram_agent/sram_driver.py create mode 100644 verify/uvm-python/sram_agent/sram_monitor.py create mode 100644 verify/uvm-python/sram_coverage/sram_coverage.py create mode 100644 verify/uvm-python/sram_interface/sram_if.py create mode 100644 verify/uvm-python/sram_item/sram_item.py create mode 100644 verify/uvm-python/sram_logger/sram_logger.py create mode 100644 verify/uvm-python/sram_ref_model/sram_ref_model.py create mode 100644 verify/uvm-python/sram_seq_lib/sram_bus_base_seq.py create mode 100644 verify/uvm-python/sram_seq_lib/sram_corners_seq.py create mode 100644 verify/uvm-python/sram_seq_lib/sram_init_seq.py create mode 100644 verify/uvm-python/sram_seq_lib/sram_ip_seq.py create mode 100644 verify/uvm-python/sram_seq_lib/sram_write_read_seq.py create mode 100644 verify/uvm-python/test_lib.py create mode 100644 verify/uvm-python/top.v diff --git a/verify/uvm-python/.gitignore b/verify/uvm-python/.gitignore new file mode 100644 index 0000000..51d311f --- /dev/null +++ b/verify/uvm-python/.gitignore @@ -0,0 +1,13 @@ +*.yalm +*.html +*.pyc +*/__pycache__/ +*.code-workspace +*.log +*.vcd +/sim/ +/coverageReports/ +*.xml +*.gtkw +/EF_UVM/ +IP_Utilities/ \ No newline at end of file diff --git a/verify/uvm-python/Makefile b/verify/uvm-python/Makefile new file mode 100644 index 0000000..e0a95b6 --- /dev/null +++ b/verify/uvm-python/Makefile @@ -0,0 +1,38 @@ +PLUSARGS += "+UVM_VERBOSITY=UVM_MEDUIM" +TOPLEVEL := top +MODULE ?= top_module +AHB_FILES ?= $(PWD)/../../hdl/bus_wrapper/SRAM_1024x32_ahb_wrapper.v $(PWD)/../../hdl/controllers/ram_ahb_controller.v +APB_FILES ?= # TODO: Add add APB wrapper file path +WB_FILES ?= $(PWD)/../../hdl/bus_wrapper/SRAM_1024x32_wb_wrapper.v $(PWD)/../../hdl/controllers/ram_wb_controller.v +HDL_FILES ?= $(PWD)/../../hdl/EF_SRAM_1024x32.v $(PWD)/../../hdl/beh_models/EF_SRAM_1024x32.tt_180V_25C.v +VERILOG_SOURCES ?= $(PWD)/top.v $(AHB_FILES) $(APB_FILES) $(WB_FILES) $(HDL_FILES) +RTL_MACROS += "-Dfunctional" # for removing timing in the behavioral model +BUS_TYPE ?= AHB +ifeq ($(BUS_TYPE),AHB) + RTL_MACROS += -DBUS_TYPE_AHB +else ifeq ($(BUS_TYPE),WISHBONE) + RTL_MACROS += -DBUS_TYPE_WISHBONE +endif +SKIP_SYNTHESIS = 1 +# RTL_MACROS ?= "-DSKIP_WAVE_DUMP" +YAML_FILE = $(PWD)/../../EF_SRAM_1024X32.yaml # TODO: update yaml file path +MAKEFLAGS += --no-print-directory + +# List of tests +TESTS := sram_corners_test sram_write_read_test + +# Variable for tag - set this as required +SIM_TAG ?= default_tag + +# Define SIM_PATH variable +SIM_PATH := $(PWD)/sim/$(SIM_TAG) + +# Check and clone EF_UVM repository at the beginning of the Makefile execution + +clone_ef_uvm := $(shell if [ ! -d "EF_UVM" ]; then \ + echo "Cloning the EF_UVM repository..."; \ + git clone https://github.com/efabless/EF_UVM.git; \ +fi;) + + +include EF_UVM/Makefile.test diff --git a/verify/uvm-python/sram_agent/sram_driver.py b/verify/uvm-python/sram_agent/sram_driver.py new file mode 100644 index 0000000..a0bcbe6 --- /dev/null +++ b/verify/uvm-python/sram_agent/sram_driver.py @@ -0,0 +1,30 @@ +from uvm.macros import uvm_component_utils, uvm_fatal, uvm_info, uvm_warning +from uvm.base.uvm_config_db import UVMConfigDb +from uvm.base.uvm_object_globals import UVM_HIGH, UVM_LOW, UVM_MEDIUM +from cocotb.triggers import Timer, ClockCycles, FallingEdge, Event, RisingEdge, First +import cocotb +import random +from EF_UVM.ip_env.ip_agent.ip_driver import ip_driver + + +class sram_driver(ip_driver): + def __init__(self, name="sram_driver", parent=None): + super().__init__(name, parent) + self.tag = name + + async def run_phase(self, phase): + uvm_info(self.tag, "run_phase started", UVM_LOW) + return + while True: + tr = [] + await self.seq_item_port.get_next_item(tr) + tr = tr[0] + # TODO: Add your code here for driving the IP + uvm_fatal( + self.tag, "please remove this line and write your code for driving here" + ) + # use self.vif. for driving interface signals + self.seq_item_port.item_done() + + +uvm_component_utils(sram_driver) diff --git a/verify/uvm-python/sram_agent/sram_monitor.py b/verify/uvm-python/sram_agent/sram_monitor.py new file mode 100644 index 0000000..7b57f8e --- /dev/null +++ b/verify/uvm-python/sram_agent/sram_monitor.py @@ -0,0 +1,32 @@ +from uvm.macros import uvm_component_utils, uvm_fatal, uvm_info, uvm_error, uvm_warning +from uvm.comps.uvm_monitor import UVMMonitor +from uvm.tlm1.uvm_analysis_port import UVMAnalysisPort +from uvm.base.uvm_config_db import UVMConfigDb +from cocotb.triggers import ( + Timer, + ClockCycles, + FallingEdge, + Event, + RisingEdge, + Combine, + First, +) +from uvm.base.uvm_object_globals import UVM_HIGH, UVM_LOW, UVM_MEDIUM +import cocotb +import math +from EF_UVM.ip_env.ip_agent.ip_monitor import ip_monitor + + +class sram_monitor(ip_monitor): + def __init__(self, name="sram_monitor", parent=None): + super().__init__(name, parent) + + async def run_phase(self, phase): + # TODO: Add logic to monitor the IP + # use self.vif. for monitoring interface signals + # self.monitor_port.write(tr) # this is the port to send the transaction after sampling it + # NOTES: how to create transaction + pass + + +uvm_component_utils(sram_monitor) diff --git a/verify/uvm-python/sram_coverage/sram_coverage.py b/verify/uvm-python/sram_coverage/sram_coverage.py new file mode 100644 index 0000000..6620d65 --- /dev/null +++ b/verify/uvm-python/sram_coverage/sram_coverage.py @@ -0,0 +1,29 @@ +from uvm.base.uvm_component import UVMComponent +from uvm.macros import uvm_component_utils +from uvm.tlm1.uvm_analysis_port import UVMAnalysisImp +from uvm.macros import uvm_component_utils, uvm_fatal, uvm_info +from uvm.base.uvm_object_globals import UVM_HIGH, UVM_LOW +from uvm.base.uvm_config_db import UVMConfigDb +from uvm.macros.uvm_tlm_defines import uvm_analysis_imp_decl +from EF_UVM.ip_env.ip_coverage.ip_coverage import ip_coverage + + +class sram_coverage(ip_coverage): + """ + component that initialize the coverage groups and control when to sample the data. + """ + + def __init__(self, name="sram_coverage", parent=None): + super().__init__(name, parent) + + def build_phase(self, phase): + super().build_phase(phase) + # TODO: initialize the class for coverage groups here + + def write(self, tr): + # called when new transaction from ip monitor is received + # TODO: Add sampling logic here + pass + + +uvm_component_utils(sram_coverage) diff --git a/verify/uvm-python/sram_interface/sram_if.py b/verify/uvm-python/sram_interface/sram_if.py new file mode 100644 index 0000000..dc4ccd5 --- /dev/null +++ b/verify/uvm-python/sram_interface/sram_if.py @@ -0,0 +1,10 @@ +from uvm.base.sv import sv_if + + +class sram_if(sv_if): + def __init__(self, dut): + return + # TODO: Add signal need to be seen by the ip monitor and driver in the following format + # bus_map = {: } + # bus_map = {"reset": "rst", ...... } + super().__init__(dut, "", bus_map) diff --git a/verify/uvm-python/sram_item/sram_item.py b/verify/uvm-python/sram_item/sram_item.py new file mode 100644 index 0000000..b29442a --- /dev/null +++ b/verify/uvm-python/sram_item/sram_item.py @@ -0,0 +1,31 @@ +from uvm.seq.uvm_sequence_item import UVMSequenceItem +from uvm.macros import ( + uvm_object_utils_begin, + uvm_object_utils_end, + uvm_field_int, + uvm_object_utils, + uvm_error, + uvm_info, +) +from uvm.base.uvm_object_globals import UVM_ALL_ON, UVM_NOPACK, UVM_HIGH, UVM_MEDIUM +from uvm.base.sv import sv +from EF_UVM.ip_env.ip_item import ip_item + + +class sram_item(ip_item): + def __init__(self, name="sram_item"): + super().__init__(name) + # TODO: Add the variables that defined the item and thier randomization status + + def convert2string(self): + # TODO: return the string representation of the item + return "" + + def do_compare(self, tr): + # method used by scoreboard to compare the items + # TODO: Add logic to compare the item with another passed item + # in the simple case this function should return (self.varaible1 == tr.variable2 and self.varaible2 == tr.variable2 and .. ) + return False + + +uvm_object_utils(sram_item) diff --git a/verify/uvm-python/sram_logger/sram_logger.py b/verify/uvm-python/sram_logger/sram_logger.py new file mode 100644 index 0000000..1e7023e --- /dev/null +++ b/verify/uvm-python/sram_logger/sram_logger.py @@ -0,0 +1,23 @@ +from EF_UVM.ip_env.ip_logger.ip_logger import ip_logger +import cocotb +from uvm.macros import uvm_component_utils, uvm_fatal + + +class sram_logger(ip_logger): + def __init__(self, name="sram_logger", parent=None): + super().__init__(name, parent) + return + uvm_fatal("sram_logger", "please write self.header in list format") + # self.header = ['Time (ns)', "Direction", "value"] + self.col_widths = [10] * len(self.header) + + def logger_formatter(self, transaction): + sim_time = f"{cocotb.utils.get_sim_time(units='ns')} ns" + # this called when new transaction is called from ip monitor + # TODO: should return the list of strings by the information in the header with the same order + return [ + sim_time, + ] + + +uvm_component_utils(sram_logger) diff --git a/verify/uvm-python/sram_ref_model/sram_ref_model.py b/verify/uvm-python/sram_ref_model/sram_ref_model.py new file mode 100644 index 0000000..27e458d --- /dev/null +++ b/verify/uvm-python/sram_ref_model/sram_ref_model.py @@ -0,0 +1,146 @@ +from uvm.base.uvm_component import UVMComponent +from uvm.macros import uvm_component_utils +from uvm.tlm1.uvm_analysis_port import UVMAnalysisImp +from uvm.base.uvm_object_globals import UVM_HIGH, UVM_LOW, UVM_MEDIUM +from uvm.macros import uvm_component_utils, uvm_fatal, uvm_info +from uvm.base.uvm_config_db import UVMConfigDb +from uvm.tlm1.uvm_analysis_port import UVMAnalysisExport +import cocotb +from EF_UVM.ref_model.ref_model import ref_model +from EF_UVM.bus_env.bus_item import bus_item +from cocotb.triggers import Event + + +class sram_ref_model(ref_model): + """ + The reference model is a crucial element within the top-level verification environment, designed to validate the functionality and performance of both the IP (Intellectual Property) and the bus system. Its primary role is to act as a representative or mimic of the actual hardware components, including the IP and the bus. Key features and functions of the reference model include: + 1) Input Simulation: The reference model is capable of receiving the same inputs that would be provided to the actual IP and bus via connection with the monitors of the bus and IP. + 2) Functional Emulation: It emulates the behavior and responses of the IP and bus under test. By replicating the operational characteristics of these components, the reference model serves as a benchmark for expected performance and behavior. + 3) Output Generation: Upon receiving inputs, the reference model processes them in a manner akin to the real hardware, subsequently generating expected outputs. These outputs are essential for comparison in the verification process. + 4) Interface with Scoreboard: The outputs from the reference model, representing the expected results, are forwarded to the scoreboard. The scoreboard then compares these expected results with the actual outputs from the IP and bus for verification. + 5)Register Abstraction Layer (RAL) Integration: The reference model includes a RAL model that mirrors the register values of the RTL, ensuring synchronization between expected and actual register states. This model facilitates register-level tests and error detection, offering accessible and up-to-date register values for other verification components. It enhances the automation and coverage of register testing, playing a vital role in ensuring the accuracy and comprehensiveness of the verification process. + """ + + def __init__(self, name="sram_ref_model", parent=None): + super().__init__(name, parent) + self.tag = name + self.ris_reg = 0 + self.mis_reg = 0 + self.irq = 0 + self.mem = Memory(size=0x1000) + + def build_phase(self, phase): + super().build_phase(phase) + # Here adding any initialize for user classes for the model + + async def run_phase(self, phase): + await super().run_phase(phase) + + def write_bus(self, tr): + # Called when new transaction is received from the bus monitor + # TODO: update the following logic to determine what to do with the received transaction + uvm_info( + self.tag, + " Ref model recieved from bus monitor: " + tr.convert2string(), + UVM_HIGH, + ) + if tr.kind == bus_item.RESET: + self.bus_bus_export.write(tr) + uvm_info("Ref model", "reset from ref model", UVM_LOW) + # TODO: write logic needed when reset is received + # self.bus_bus_export.write(tr) + return + if tr.kind == bus_item.WRITE: + uvm_info("Ref model", f"write {tr.data} to {tr.addr}", UVM_LOW) + if tr.size == bus_item.WORD_ACCESS: + self.mem.write_word(tr.addr, tr.data) + elif tr.size == bus_item.HALF_WORD_ACCESS: + if tr.addr % 4 == 0: + self.mem.write_halfword(tr.addr, tr.data & 0xFFFF) + else: + self.mem.write_halfword(tr.addr, tr.data >> 16) + elif tr.size == bus_item.BYTE_ACCESS: + if tr.addr % 4 == 0: + self.mem.write_byte(tr.addr, tr.data & 0xFF) + elif tr.addr % 4 == 1: + self.mem.write_byte(tr.addr, (tr.data >> 8) & 0xFF) + elif tr.addr % 4 == 2: + self.mem.write_byte(tr.addr, (tr.data >> 16) & 0xFF) + elif tr.addr % 4 == 3: + self.mem.write_byte(tr.addr, (tr.data >> 24) & 0xFF) + self.bus_bus_export.write(tr) # this is output to the scoreboard + elif tr.kind == bus_item.READ: + td = tr.do_clone() + if tr.size == bus_item.WORD_ACCESS: + td.data = self.mem.read_word(tr.addr) + elif tr.size == bus_item.HALF_WORD_ACCESS: + td.data = self.mem.read_halfword(tr.addr) + elif tr.size == bus_item.BYTE_ACCESS: + td.data = self.mem.read_byte(tr.addr) + self.bus_bus_export.write(td) # this is output to the scoreboard + + def write_ip(self, tr): + # Called when new transaction is received from the ip monitor + # TODO: write what to do when new transaction ip transaction is received + uvm_info( + self.tag, + "Ref model recieved from ip monitor: " + tr.convert2string(), + UVM_HIGH, + ) + + +uvm_component_utils(sram_ref_model) + + +class Memory: + def __init__(self, size=0x1000): + """Initialize the memory array with the given size.""" + self.size = size + self.memory = bytearray(size) + + def _check_address(self, address, length): + """Check if the address is within bounds.""" + if address < 0 or address + length > self.size: + raise ValueError(f"Address {hex(address)} out of bounds.") + + def read_byte(self, address): + """Read a single byte from memory.""" + self._check_address(address, 1) + uvm_info("Ref model", f"read_byte {hex(address)}", UVM_LOW) + return self.memory[address] + + def write_byte(self, address, value): + """Write a single byte to memory.""" + self._check_address(address, 1) + uvm_info("Ref model", f"write_byte {hex(value)} to {hex(address)}", UVM_LOW) + self.memory[address] = value & 0xFF + + def read_halfword(self, address): + """Read 2 bytes (half-word) from memory as two byte reads.""" + self._check_address(address, 2) + byte0 = self.read_byte(address) + byte1 = self.read_byte(address + 1) + return byte0 | (byte1 << 8) + + def write_halfword(self, address, value): + """Write 2 bytes (half-word) to memory as two byte writes.""" + self._check_address(address, 2) + self.write_byte(address, value & 0xFF) # Lower byte + self.write_byte(address + 1, (value >> 8) & 0xFF) # Upper byte + + def read_word(self, address): + """Read 4 bytes (word) from memory as four byte reads.""" + self._check_address(address, 4) + byte0 = self.read_byte(address) + byte1 = self.read_byte(address + 1) + byte2 = self.read_byte(address + 2) + byte3 = self.read_byte(address + 3) + return byte0 | (byte1 << 8) | (byte2 << 16) | (byte3 << 24) + + def write_word(self, address, value): + """Write 4 bytes (word) to memory as four byte writes.""" + self._check_address(address, 4) + self.write_byte(address, value & 0xFF) # Lowest byte + self.write_byte(address + 1, (value >> 8) & 0xFF) # Second byte + self.write_byte(address + 2, (value >> 16) & 0xFF) # Third byte + self.write_byte(address + 3, (value >> 24) & 0xFF) # Highest byte \ No newline at end of file diff --git a/verify/uvm-python/sram_seq_lib/sram_bus_base_seq.py b/verify/uvm-python/sram_seq_lib/sram_bus_base_seq.py new file mode 100644 index 0000000..41f7753 --- /dev/null +++ b/verify/uvm-python/sram_seq_lib/sram_bus_base_seq.py @@ -0,0 +1,55 @@ +from uvm.seq import UVMSequence +from uvm.macros.uvm_object_defines import uvm_object_utils +from uvm.macros.uvm_message_defines import uvm_fatal +from uvm.base.uvm_config_db import UVMConfigDb +from EF_UVM.bus_env.bus_seq_lib.bus_seq_base import bus_seq_base +from cocotb.triggers import Timer +from uvm.macros.uvm_sequence_defines import uvm_do_with, uvm_do +import random +from EF_UVM.bus_env.bus_item import bus_item + + +class sram_bus_base_seq(bus_seq_base): + # use this sequence write or read from register by the bus interface + # this sequence should be connected to the bus sequencer in the testbench + # you should create as many sequences as you need not only this one + def __init__(self, name="sram_bus_seq", mem_size=0x400): + super().__init__(name) + self.valid_addr = set() + self.mem_size = mem_size + + async def body(self): + await super().body() + + async def write_to_mem(self, address=None, data=None, size=None): + self.create_new_item() + self.req.rand_mode(0) + if address is None: + address = random.randint(0, self.mem_size - 1) << 2 + self.req.addr = address + self.req.data = random.randint(0, 0xFFFFFFFF) if data is None else data + self.req.kind = bus_item.WRITE + self.req.size = bus_item.WORD_ACCESS if size is None else size + await uvm_do(self, self.req) + self.valid_addr.add(address) + if self.req.size == bus_item.WORD_ACCESS: + self.valid_addr.add(address + 1) + self.valid_addr.add(address + 2) + self.valid_addr.add(address + 3) + elif self.req.size == bus_item.HALF_WORD_ACCESS: + self.valid_addr.add(address + 1) + + async def read_from_mem(self, address=None, size=None): + self.create_new_item() + self.req.rand_mode(0) + if address is None: + address = random.randint(0, self.mem_size - 1) << 2 + self.req.addr = address + self.req.data = 0 + self.req.kind = bus_item.READ + self.req.size = bus_item.WORD_ACCESS if size is None else size + await uvm_do(self, self.req) + + + +uvm_object_utils(sram_bus_base_seq) diff --git a/verify/uvm-python/sram_seq_lib/sram_corners_seq.py b/verify/uvm-python/sram_seq_lib/sram_corners_seq.py new file mode 100644 index 0000000..b671195 --- /dev/null +++ b/verify/uvm-python/sram_seq_lib/sram_corners_seq.py @@ -0,0 +1,98 @@ +from uvm.seq import UVMSequence +from uvm.macros.uvm_object_defines import uvm_object_utils +from uvm.macros.uvm_message_defines import uvm_fatal +from uvm.base.uvm_config_db import UVMConfigDb +from EF_UVM.bus_env.bus_seq_lib.bus_seq_base import bus_seq_base +from cocotb.triggers import Timer +from uvm.macros.uvm_sequence_defines import uvm_do_with, uvm_do +import random +from EF_UVM.bus_env.bus_item import bus_item +from sram_seq_lib.sram_bus_base_seq import sram_bus_base_seq +from sram_seq_lib.sram_init_seq import sram_init_seq + +class sram_corners_seq(sram_bus_base_seq): + # use this sequence write or read from register by the bus interface + # this sequence should be connected to the bus sequencer in the testbench + # you should create as many sequences as you need not only this one + def __init__(self, name="sram_corners_seq", mem_size=0x400): + super().__init__(name, mem_size) + + async def body(self): + await super().body() + await uvm_do_with(self, sram_init_seq("sram_init_seq", self.mem_size)) + await uvm_do_with(self, sram_same_address_seq("sram_same_address_seq", self.mem_size)) + await uvm_do_with(self, sram_one_zeros_seq("sram_one_zeros_seq", self.mem_size)) + await uvm_do_with(self, sram_lowest_highest_seq("sram_lowest_highest_seq", self.mem_size)) + + +uvm_object_utils(sram_corners_seq) + + +class sram_same_address_seq(sram_bus_base_seq): + def __init__(self, name="sram_same_address_seq", mem_size=0x400): + super().__init__(name, mem_size) + + async def body(self): + await super().body() + for __ in range(10): + pick_address = random.randint(0, self.mem_size - 1) << 2 + # randomly read and write to the same address 20 times + for _ in range(25): + if random.random() >= 0.5: + await self.write_to_mem(address=pick_address) + else: + await self.read_from_mem(address=pick_address) + + +uvm_object_utils(sram_same_address_seq) + + +class sram_one_zeros_seq(sram_bus_base_seq): + def __init__(self, name="sram_one_zeros_seq", mem_size=0x400): + super().__init__(name, mem_size) + + async def body(self): + await super().body() + + for _ in range(self.mem_size): + data = random.choice( + (0x0, 0xAAAAAAAA, 0x55555555, 0xFFFFFFFF, 0x33333333, 0xCCCCCCCC) + ) + await self.write_to_mem(data=data) + + for _ in range(self.mem_size): + await self.read_from_mem() + + +uvm_object_utils(sram_same_address_seq) + + +class sram_lowest_highest_seq(sram_bus_base_seq): + def __init__(self, name="sram_one_zeros_seq", mem_size=0x400): + super().__init__(name, mem_size) + + async def body(self): + await super().body() + # initialize with 0 + for _ in range(self.mem_size): + await self.write_to_mem(data=0) + + # write then read small data + for _ in range(self.mem_size): + await self.write_to_mem(data=random.getrandbits(5)) + + for _ in range(self.mem_size): + await self.read_from_mem() + + # initialize with 0 + for _ in range(self.mem_size): + await self.write_to_mem(data=0) + + # write then read large data + for _ in range(self.mem_size): + await self.write_to_mem(data=random.randint(2**25, 2**32 - 1)) + + for _ in range(self.mem_size): + await self.read_from_mem() + +uvm_object_utils(sram_lowest_highest_seq) diff --git a/verify/uvm-python/sram_seq_lib/sram_init_seq.py b/verify/uvm-python/sram_seq_lib/sram_init_seq.py new file mode 100644 index 0000000..031076e --- /dev/null +++ b/verify/uvm-python/sram_seq_lib/sram_init_seq.py @@ -0,0 +1,34 @@ +from uvm.seq import UVMSequence +from uvm.macros.uvm_object_defines import uvm_object_utils +from uvm.macros.uvm_message_defines import uvm_fatal +from uvm.base.uvm_config_db import UVMConfigDb +from EF_UVM.bus_env.bus_seq_lib.bus_seq_base import bus_seq_base +from cocotb.triggers import Timer +from uvm.macros.uvm_sequence_defines import uvm_do_with, uvm_do +import random +from EF_UVM.bus_env.bus_item import bus_item +from sram_seq_lib.sram_bus_base_seq import sram_bus_base_seq + +class sram_init_seq(sram_bus_base_seq): + # use this sequence write or read from register by the bus interface + # this sequence should be connected to the bus sequencer in the testbench + # you should create as many sequences as you need not only this one + def __init__(self, name="sram_bus_seq", mem_size=0x400): + super().__init__(name, mem_size) + + async def body(self): + await super().body() + # Add the sequqnce here + # you could use method send_req to send a write or read using the register name + # example for writing register by value > 5 + # await self.send_req(is_write=True, reg="control", data_condition=lambda data: data > 5) + # example for writing register by value == 5 + # await self.send_req(is_write=True, reg="control", data_value=5) + # example for reading register from register + # await self.send_req(is_write=False, reg="control") + for address in range(0, self.mem_size * 4, 4): + await self.write_to_mem(address, size=bus_item.WORD_ACCESS) + await self.send_nop() + + +uvm_object_utils(sram_init_seq) diff --git a/verify/uvm-python/sram_seq_lib/sram_ip_seq.py b/verify/uvm-python/sram_seq_lib/sram_ip_seq.py new file mode 100644 index 0000000..910fe56 --- /dev/null +++ b/verify/uvm-python/sram_seq_lib/sram_ip_seq.py @@ -0,0 +1,30 @@ +from uvm.macros.uvm_object_defines import uvm_object_utils +from uvm.macros.uvm_sequence_defines import uvm_do_with, uvm_do +from uvm.base import sv, UVM_HIGH, UVM_LOW +from uvm.macros.uvm_message_defines import uvm_info, uvm_fatal +from sram_item.sram_item import sram_item +from uvm.seq import UVMSequence + + +class sram_ip_seq(UVMSequence): + # use this sequence write or read from register by the ip interface + # this sequence should be connected to the ip sequencer in the testbench + # you should add as many sequences as you need not only this one + def __init__(self, name="sram_ip_seq"): + UVMSequence.__init__(self, name) + self.set_automatic_phase_objection(1) + self.req = sram_item() + self.rsp = sram_item() + self.tag = name + + async def body(self): + # Add sequence to be used by the ip sequencer + # you could use method uvm_do and uvm_do_with to send these transactions + # send item with conditions + # await uvm_do_with(self, self.req, lambda sram_var1: sram_var1 == 10, lambda sram_var2: sram_var2 > 7, ......) + # send item without conditions + # await uvm_do(self, self.req) + pass + + +uvm_object_utils(sram_ip_seq) diff --git a/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py b/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py new file mode 100644 index 0000000..d03d9f7 --- /dev/null +++ b/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py @@ -0,0 +1,56 @@ +from uvm.seq import UVMSequence +from uvm.macros.uvm_object_defines import uvm_object_utils +from uvm.macros.uvm_message_defines import uvm_fatal +from uvm.base.uvm_config_db import UVMConfigDb +from EF_UVM.bus_env.bus_seq_lib.bus_seq_base import bus_seq_base +from cocotb.triggers import Timer +from uvm.macros.uvm_sequence_defines import uvm_do_with, uvm_do +import random +from EF_UVM.bus_env.bus_item import bus_item +from sram_seq_lib.sram_bus_base_seq import sram_bus_base_seq +from sram_seq_lib.sram_init_seq import sram_init_seq +import cocotb + +class sram_write_read_seq(sram_bus_base_seq): + # use this sequence write or read from register by the bus interface + # this sequence should be connected to the bus sequencer in the testbench + # you should create as many sequences as you need not only this one + def __init__(self, name="sram_corners_seq", mem_size=0x400): + super().__init__(name, mem_size) + + async def body(self): + await super().body() + await self.write_to_mem(address=0) + await self.write_to_mem(address=0x4) + await self.write_to_mem(address=0x8) + await self.read_from_mem(address=0) + await self.read_from_mem(address=0x4) + await self.read_from_mem(address=0x8) + await uvm_do_with(self, sram_init_seq("sram_init_seq", self.mem_size)) + await self._write_read_seq(self.mem_size * 2, 0.5) + + async def _write_read_seq(self, iterations, write_priority): + for _ in range(iterations): + address, size = self.rand_addr_size() + if random.random() >= write_priority: + await self.write_to_mem(address=address, size=size) + else: + await self.read_from_mem(address=address, size=size) + + def rand_addr_size(self): + BUS_TYPE = cocotb.plusargs["BUS_TYPE"] + if BUS_TYPE == "AHB": + address = random.randint(0, (self.mem_size - 1) * 4) + if address % 4 == 0: + size = random.choice((bus_item.WORD_ACCESS, bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS)) + elif address % 2 == 0: + size = random.choice((bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS)) + else: + size = bus_item.BYTE_ACCESS + else: # sizes are fixed for APB and WB + address = random.randint(0, self.mem_size - 1) << 2 + size = bus_item.WORD_ACCESS + + return address, size + +uvm_object_utils(sram_write_read_seq) diff --git a/verify/uvm-python/test_lib.py b/verify/uvm-python/test_lib.py new file mode 100644 index 0000000..b1feabe --- /dev/null +++ b/verify/uvm-python/test_lib.py @@ -0,0 +1,130 @@ +import cocotb +from uvm.macros import uvm_component_utils, uvm_fatal, uvm_info +from uvm.base.uvm_config_db import UVMConfigDb +from uvm.base.uvm_object_globals import UVM_LOW +from uvm.base.uvm_globals import run_test +from sram_interface.sram_if import sram_if +from EF_UVM.bus_env.bus_interface.bus_if import ( + bus_apb_if, + bus_irq_if, + bus_ahb_if, + bus_wb_if, +) +from cocotb_coverage.coverage import coverage_db +from cocotb.triggers import Event, First +from EF_UVM.bus_env.bus_regs import bus_regs +from uvm.base import UVMRoot +from EF_UVM.base_test import base_test + +# seqences import +from sram_seq_lib.sram_corners_seq import sram_corners_seq +from sram_seq_lib.sram_write_read_seq import sram_write_read_seq +from sram_seq_lib.sram_ip_seq import sram_ip_seq + +# override classes +from EF_UVM.ip_env.ip_agent.ip_driver import ip_driver +from sram_agent.sram_driver import sram_driver +from EF_UVM.ip_env.ip_agent.ip_monitor import ip_monitor +from sram_agent.sram_monitor import sram_monitor +from EF_UVM.ref_model.ref_model import ref_model +from sram_ref_model.sram_ref_model import sram_ref_model +from EF_UVM.ip_env.ip_coverage.ip_coverage import ip_coverage +from sram_coverage.sram_coverage import sram_coverage +from EF_UVM.ip_env.ip_logger.ip_logger import ip_logger +from sram_logger.sram_logger import sram_logger + + +@cocotb.test() +async def module_top(dut): + # profiler = cProfile.Profile() + # profiler.enable() + BUS_TYPE = cocotb.plusargs["BUS_TYPE"] + pif = sram_if(dut) + if BUS_TYPE == "APB": + w_if = bus_apb_if(dut) + elif BUS_TYPE == "AHB": + w_if = bus_ahb_if(dut) + elif BUS_TYPE == "WISHBONE": + w_if = bus_wb_if(dut) + else: + uvm_fatal("module_top", f"unknown bus type {BUS_TYPE}") + # w_irq_if = bus_irq_if(dut) + UVMConfigDb.set(None, "*", "ip_if", pif) + UVMConfigDb.set(None, "*", "bus_if", w_if) + # UVMConfigDb.set(None, "*", "bus_irq_if", w_irq_if) + yaml_file = [] + UVMRoot().clp.get_arg_values("+YAML_FILE=", yaml_file) + yaml_file = yaml_file[0] + regs = bus_regs(yaml_file) + UVMConfigDb.set(None, "*", "bus_regs", regs) + UVMConfigDb.set(None, "*", "irq_exist", regs.get_irq_exist()) + UVMConfigDb.set(None, "*", "collect_coverage", True) + UVMConfigDb.set(None, "*", "disable_logger", False) + test_path = [] + UVMRoot().clp.get_arg_values("+TEST_PATH=", test_path) + test_path = test_path[0] + await run_test() + coverage_db.export_to_yaml(filename=f"{test_path}/coverage.yalm") + # profiler.disable() + # profiler.dump_stats("profile_result.prof") + + +class sram_base_test(base_test): + def __init__(self, name="sram_first_test", parent=None): + BUS_TYPE = cocotb.plusargs["BUS_TYPE"] + super().__init__(name, bus_type=BUS_TYPE, parent=parent) + self.tag = name + + def build_phase(self, phase): + super().build_phase(phase) + # override + self.set_type_override_by_type(ip_driver.get_type(), sram_driver.get_type()) + self.set_type_override_by_type( + ip_monitor.get_type(), sram_monitor.get_type() + ) + self.set_type_override_by_type( + ref_model.get_type(), sram_ref_model.get_type() + ) + self.set_type_override_by_type( + ip_coverage.get_type(), sram_coverage.get_type() + ) + self.set_type_override_by_type(ip_logger.get_type(), sram_logger.get_type()) + + +uvm_component_utils(sram_base_test) + + +class sram_corners_test(sram_base_test): + def __init__(self, name="sram__first_test", parent=None): + super().__init__(name, parent=parent) + self.tag = name + + async def main_phase(self, phase): + uvm_info(self.tag, f"Starting test {self.__class__.__name__}", UVM_LOW) + phase.raise_objection(self, f"{self.__class__.__name__} OBJECTED") + # TODO: conntect sequence with sequencer here + # for example if you need to run the 2 sequence sequentially + bus_seq = sram_corners_seq("sram_corners_seq", mem_size=0x400) + await bus_seq.start(self.bus_sqr) + phase.drop_objection(self, f"{self.__class__.__name__} drop objection") + + +uvm_component_utils(sram_corners_test) + + +class sram_write_read_test(sram_base_test): + def __init__(self, name="sram__first_test", parent=None): + super().__init__(name, parent=parent) + self.tag = name + + async def main_phase(self, phase): + uvm_info(self.tag, f"Starting test {self.__class__.__name__}", UVM_LOW) + phase.raise_objection(self, f"{self.__class__.__name__} OBJECTED") + # TODO: conntect sequence with sequencer here + # for example if you need to run the 2 sequence sequentially + bus_seq = sram_write_read_seq("sram_corners_seq", mem_size=0x400) + await bus_seq.start(self.bus_sqr) + phase.drop_objection(self, f"{self.__class__.__name__} drop objection") + + +uvm_component_utils(sram_write_read_test) diff --git a/verify/uvm-python/top.v b/verify/uvm-python/top.v new file mode 100644 index 0000000..0ee1c2e --- /dev/null +++ b/verify/uvm-python/top.v @@ -0,0 +1,54 @@ +`timescale 1ns/1ps + +module top(); + reg CLK = 0; + wire RESETn = 1; + wire irq; + // TODO: Add any IP signals here + `ifdef BUS_TYPE_APB + wire [31:0] PADDR; + wire PWRITE; + wire PSEL; + wire PENABLE; + wire [31:0] PWDATA; + wire [31:0] PRDATA; + wire PREADY; + // TODO: initialize the ABP wrapper here + // for example + // EF_sram_APB dut(.PCLK(CLK), .PRESETn(RESETn), .PADDR(PADDR), .PWRITE(PWRITE), .PSEL(PSEL), .PENABLE(PENABLE), .PWDATA(PWDATA), .PRDATA(PRDATA), .PREADY(PREADY), .IRQ(irq)); + `endif // BUS_TYPE_APB + `ifdef BUS_TYPE_AHB + wire [31:0] HADDR; + wire HWRITE; + wire HSEL = 0; + wire HREADYOUT; + wire [1:0] HTRANS=0; + wire [31:0] HWDATA; + wire [31:0] HRDATA; + wire HREADY; + wire [2:0] HSIZE; + // TODO: initialize the AHB wrapper here + // for example + SRAM_1024x32_ahb_wrapper dut(.HCLK(CLK), .HRESETn(RESETn), .HADDR(HADDR), .HWRITE(HWRITE), .HSIZE(HSIZE), .HSEL(HSEL), .HTRANS(HTRANS), .HWDATA(HWDATA), .HRDATA(HRDATA), .HREADY(HREADY),.HREADYOUT(HREADYOUT)); + `endif // BUS_TYPE_AHB + `ifdef BUS_TYPE_WISHBONE + wire [31:0] adr_i; + wire [31:0] dat_i; + wire [31:0] dat_o; + wire [3:0] sel_i; + wire cyc_i; + wire stb_i; + reg ack_o; + // TODO: initialize the Wishbone wrapper here + // for example + SRAM_1024x32_wb_wrapper dut(.wb_clk_i(CLK), .wb_rst_i(~RESETn), .wbs_adr_i(adr_i), .wbs_dat_i(dat_i), .wbs_dat_o(dat_o), .wbs_sel_i(sel_i), .wbs_cyc_i(cyc_i), .wbs_stb_i(stb_i), .wbs_ack_o(ack_o), .wbs_we_i(we_i)); + `endif // BUS_TYPE_WISHBONE + // monitor inside signals + `ifndef SKIP_WAVE_DUMP + initial begin + $dumpfile ({"waves.vcd"}); + $dumpvars(0, top); + end + `endif + always #10 CLK = !CLK; // clk generator +endmodule \ No newline at end of file From fc457d8350768715ee4364e70cbf675892023b34 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 20:00:09 +0300 Subject: [PATCH 3/9] Add CI for uvm --- .github/workflows/uvm_ci.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/uvm_ci.yaml diff --git a/.github/workflows/uvm_ci.yaml b/.github/workflows/uvm_ci.yaml new file mode 100644 index 0000000..b7253e4 --- /dev/null +++ b/.github/workflows/uvm_ci.yaml @@ -0,0 +1,26 @@ +name: Run UVM tests + +on: + push: # This now triggers on pushes to any branch + pull_request: # This now triggers on pull requests to any branch + +jobs: + Extract-Buses: + runs-on: ubuntu-latest + outputs: + IPs: ${{ steps.set-IPs-matrix.outputs.IPs }} + buses: ${{ steps.extract_buses.outputs.buses }} + steps: + - name: Extract Supported Buses + id: extract_buses + uses: efabless/EF_UVM/.github/actions/get-bus@main + - name: Check Output + run: echo ${{ steps.extract_buses.outputs.buses }} + Run-IP-Tests: + uses: efabless/EF_UVM/.github/workflows/run_IP.yaml@main + needs: [Extract-Buses] + with: + test-names: "all_tests" + name: ${{ github.event.repository.name }} + buses: ${{ needs.Extract-Buses.outputs.buses }} + is-ip: true \ No newline at end of file From 9dad7a68453375c4804f5c20229536b6c8e9e9e5 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 20:04:42 +0300 Subject: [PATCH 4/9] correct the yaml file name to match the repo name --- EF_SRAM_1024X32.yaml => EF_SRAM_1024x32.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename EF_SRAM_1024X32.yaml => EF_SRAM_1024x32.yaml (100%) diff --git a/EF_SRAM_1024X32.yaml b/EF_SRAM_1024x32.yaml similarity index 100% rename from EF_SRAM_1024X32.yaml rename to EF_SRAM_1024x32.yaml From aed076eaee8b1b782f175aa1fd2ca596979cf8eb Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 20:05:50 +0300 Subject: [PATCH 5/9] correct BUS name typo --- EF_SRAM_1024x32.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EF_SRAM_1024x32.yaml b/EF_SRAM_1024x32.yaml index 9475560..8b8da63 100644 --- a/EF_SRAM_1024x32.yaml +++ b/EF_SRAM_1024x32.yaml @@ -15,7 +15,7 @@ info: - memory bus: - WB - - AHB + - AHBL type: hard status: verified width: 387.87 From f31b06c7518659fd97ba9b6b48d7dcdb3d8a51cb Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 20:09:22 +0300 Subject: [PATCH 6/9] correct yaml file name typo in makefile --- verify/uvm-python/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verify/uvm-python/Makefile b/verify/uvm-python/Makefile index e0a95b6..7ec2fc0 100644 --- a/verify/uvm-python/Makefile +++ b/verify/uvm-python/Makefile @@ -15,7 +15,7 @@ else ifeq ($(BUS_TYPE),WISHBONE) endif SKIP_SYNTHESIS = 1 # RTL_MACROS ?= "-DSKIP_WAVE_DUMP" -YAML_FILE = $(PWD)/../../EF_SRAM_1024X32.yaml # TODO: update yaml file path +YAML_FILE = $(PWD)/../../EF_SRAM_1024x32.yaml # TODO: update yaml file path MAKEFLAGS += --no-print-directory # List of tests From 9baab6d2cf929debeef139689ceb7f75ae8f3def Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 20:19:46 +0300 Subject: [PATCH 7/9] Add global varaiable for the size of the memory --- verify/uvm-python/test_lib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/verify/uvm-python/test_lib.py b/verify/uvm-python/test_lib.py index b1feabe..2be655a 100644 --- a/verify/uvm-python/test_lib.py +++ b/verify/uvm-python/test_lib.py @@ -74,6 +74,7 @@ def __init__(self, name="sram_first_test", parent=None): BUS_TYPE = cocotb.plusargs["BUS_TYPE"] super().__init__(name, bus_type=BUS_TYPE, parent=parent) self.tag = name + self.memory_size = 0x400 def build_phase(self, phase): super().build_phase(phase) @@ -104,7 +105,7 @@ async def main_phase(self, phase): phase.raise_objection(self, f"{self.__class__.__name__} OBJECTED") # TODO: conntect sequence with sequencer here # for example if you need to run the 2 sequence sequentially - bus_seq = sram_corners_seq("sram_corners_seq", mem_size=0x400) + bus_seq = sram_corners_seq("sram_corners_seq", mem_size=self.memory_size) await bus_seq.start(self.bus_sqr) phase.drop_objection(self, f"{self.__class__.__name__} drop objection") @@ -122,7 +123,7 @@ async def main_phase(self, phase): phase.raise_objection(self, f"{self.__class__.__name__} OBJECTED") # TODO: conntect sequence with sequencer here # for example if you need to run the 2 sequence sequentially - bus_seq = sram_write_read_seq("sram_corners_seq", mem_size=0x400) + bus_seq = sram_write_read_seq("sram_corners_seq", mem_size=self.memory_size) await bus_seq.start(self.bus_sqr) phase.drop_objection(self, f"{self.__class__.__name__} drop objection") From 255726e1c93d0007ab88f242ba1058322820c2ad Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Thu, 17 Oct 2024 14:23:52 +0300 Subject: [PATCH 8/9] Add flag to skip gl sims --- .github/workflows/uvm_ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/uvm_ci.yaml b/.github/workflows/uvm_ci.yaml index b7253e4..02ae32d 100644 --- a/.github/workflows/uvm_ci.yaml +++ b/.github/workflows/uvm_ci.yaml @@ -23,4 +23,5 @@ jobs: test-names: "all_tests" name: ${{ github.event.repository.name }} buses: ${{ needs.Extract-Buses.outputs.buses }} - is-ip: true \ No newline at end of file + is-ip: true + skip-gl: yes \ No newline at end of file From 72b052ede331e4d6d76a7a1819c0fe2a6fd561c8 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Thu, 17 Oct 2024 15:47:43 +0300 Subject: [PATCH 9/9] support diffrent sizes with wishbone --- .../sram_ref_model/sram_ref_model.py | 14 ++----------- .../sram_seq_lib/sram_write_read_seq.py | 20 +++++++------------ 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/verify/uvm-python/sram_ref_model/sram_ref_model.py b/verify/uvm-python/sram_ref_model/sram_ref_model.py index 27e458d..8457af1 100644 --- a/verify/uvm-python/sram_ref_model/sram_ref_model.py +++ b/verify/uvm-python/sram_ref_model/sram_ref_model.py @@ -55,19 +55,9 @@ def write_bus(self, tr): if tr.size == bus_item.WORD_ACCESS: self.mem.write_word(tr.addr, tr.data) elif tr.size == bus_item.HALF_WORD_ACCESS: - if tr.addr % 4 == 0: - self.mem.write_halfword(tr.addr, tr.data & 0xFFFF) - else: - self.mem.write_halfword(tr.addr, tr.data >> 16) + self.mem.write_halfword(tr.addr, tr.data & 0xFFFF) elif tr.size == bus_item.BYTE_ACCESS: - if tr.addr % 4 == 0: - self.mem.write_byte(tr.addr, tr.data & 0xFF) - elif tr.addr % 4 == 1: - self.mem.write_byte(tr.addr, (tr.data >> 8) & 0xFF) - elif tr.addr % 4 == 2: - self.mem.write_byte(tr.addr, (tr.data >> 16) & 0xFF) - elif tr.addr % 4 == 3: - self.mem.write_byte(tr.addr, (tr.data >> 24) & 0xFF) + self.mem.write_byte(tr.addr, tr.data & 0xFF) self.bus_bus_export.write(tr) # this is output to the scoreboard elif tr.kind == bus_item.READ: td = tr.do_clone() diff --git a/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py b/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py index d03d9f7..bfe77ce 100644 --- a/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py +++ b/verify/uvm-python/sram_seq_lib/sram_write_read_seq.py @@ -38,19 +38,13 @@ async def _write_read_seq(self, iterations, write_priority): await self.read_from_mem(address=address, size=size) def rand_addr_size(self): - BUS_TYPE = cocotb.plusargs["BUS_TYPE"] - if BUS_TYPE == "AHB": - address = random.randint(0, (self.mem_size - 1) * 4) - if address % 4 == 0: - size = random.choice((bus_item.WORD_ACCESS, bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS)) - elif address % 2 == 0: - size = random.choice((bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS)) - else: - size = bus_item.BYTE_ACCESS - else: # sizes are fixed for APB and WB - address = random.randint(0, self.mem_size - 1) << 2 - size = bus_item.WORD_ACCESS - + address = random.randint(0, (self.mem_size - 1) * 4) + if address % 4 == 0: + size = random.choice((bus_item.WORD_ACCESS, bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS)) + elif address % 2 == 0: + size = random.choice((bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS)) + else: + size = bus_item.BYTE_ACCESS return address, size uvm_object_utils(sram_write_read_seq)