From 1a1105df33473d6f0f24e4c9dda5e87a91221aa3 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 10:16:25 +0300 Subject: [PATCH 1/2] Add tests for PSRAM --- EF_PSRAM_CTRL_V2.yaml | 16 +- ip/.gitignore | 3 + ip/dependencies.json | 7 + verify/uvm-python/.gitignore | 13 + verify/uvm-python/23LC1024.v | 612 ++++++++++++++++++ verify/uvm-python/Makefile | 52 ++ verify/uvm-python/psram_agent/psram_driver.py | 30 + .../uvm-python/psram_agent/psram_monitor.py | 32 + verify/uvm-python/psram_bus_item.py | 14 + .../psram_coverage/psram_coverage.py | 29 + verify/uvm-python/psram_interface/psram_if.py | 9 + verify/uvm-python/psram_item/psram_item.py | 31 + .../uvm-python/psram_logger/psram_logger.py | 23 + .../psram_ref_model/psram_ref_model.py | 157 +++++ .../psram_seq_lib/psram_base_seq.py | 149 +++++ .../uvm-python/psram_seq_lib/psram_ip_seq.py | 30 + .../uvm-python/psram_seq_lib/psram_sdi_seq.py | 30 + .../uvm-python/psram_seq_lib/psram_spi_seq.py | 27 + .../uvm-python/psram_seq_lib/psram_sqi_seq.py | 30 + verify/uvm-python/test_lib.py | 195 ++++++ verify/uvm-python/top.v | 54 ++ 21 files changed, 1535 insertions(+), 8 deletions(-) create mode 100644 ip/.gitignore create mode 100644 ip/dependencies.json create mode 100644 verify/uvm-python/.gitignore create mode 100644 verify/uvm-python/23LC1024.v create mode 100644 verify/uvm-python/Makefile create mode 100644 verify/uvm-python/psram_agent/psram_driver.py create mode 100644 verify/uvm-python/psram_agent/psram_monitor.py create mode 100644 verify/uvm-python/psram_bus_item.py create mode 100644 verify/uvm-python/psram_coverage/psram_coverage.py create mode 100644 verify/uvm-python/psram_interface/psram_if.py create mode 100644 verify/uvm-python/psram_item/psram_item.py create mode 100644 verify/uvm-python/psram_logger/psram_logger.py create mode 100644 verify/uvm-python/psram_ref_model/psram_ref_model.py create mode 100644 verify/uvm-python/psram_seq_lib/psram_base_seq.py create mode 100644 verify/uvm-python/psram_seq_lib/psram_ip_seq.py create mode 100644 verify/uvm-python/psram_seq_lib/psram_sdi_seq.py create mode 100644 verify/uvm-python/psram_seq_lib/psram_spi_seq.py create mode 100644 verify/uvm-python/psram_seq_lib/psram_sqi_seq.py create mode 100644 verify/uvm-python/test_lib.py create mode 100644 verify/uvm-python/top.v diff --git a/EF_PSRAM_CTRL_V2.yaml b/EF_PSRAM_CTRL_V2.yaml index 159ba31..76fa32f 100644 --- a/EF_PSRAM_CTRL_V2.yaml +++ b/EF_PSRAM_CTRL_V2.yaml @@ -63,7 +63,7 @@ registers: fifo: no offset: 0x0080_0100 bit_access: no - init: "3" + init: "'h3" write_port: "" description: RD Command Register - name: wr_cmd @@ -72,7 +72,7 @@ registers: fifo: no offset: 0x0080_0200 bit_access: no - init: "2" + init: "'h2" write_port: "" description: WR Command Register - name: eqpi_cmd @@ -81,7 +81,7 @@ registers: fifo: no offset: 0x0080_0400 bit_access: no - init: "35" + init: "'h35" write_port: "" description: Enter QPI Command Register - name: xqpi_cmd @@ -90,7 +90,7 @@ registers: fifo: no offset: 0x0080_0800 bit_access: no - init: "FE" + init: "'hFE" write_port: "" description: Exit QPI Command Register - name: wait_states @@ -99,7 +99,7 @@ registers: fifo: no offset: 0x0080_1000 bit_access: no - init: "0" + init: "'h0" write_port: "" description: Wait States Register - name: mode @@ -108,7 +108,7 @@ registers: fifo: no offset: 0x0080_2000 bit_access: no - init: "0" + init: "'h0" write_port: "" description: I/O Mode Register, {qpi, qspi} - name: enter_qpi @@ -117,7 +117,7 @@ registers: fifo: no offset: 0x0080_4000 bit_access: no - init: "0" + init: "'h0" write_port: "" description: Initiate Enter QPI (EQPI) Mode process Register - name: exit_qpi @@ -126,7 +126,7 @@ registers: fifo: no offset: 0x0080_8000 bit_access: no - init: "0" + init: "'h0" write_port: "" description: Initiate Exit QPI (XQPI) Mode process Register clock: HCLK diff --git a/ip/.gitignore b/ip/.gitignore new file mode 100644 index 0000000..83fe78c --- /dev/null +++ b/ip/.gitignore @@ -0,0 +1,3 @@ +* +!dependencies.json +!.gitignore diff --git a/ip/dependencies.json b/ip/dependencies.json new file mode 100644 index 0000000..bc86f84 --- /dev/null +++ b/ip/dependencies.json @@ -0,0 +1,7 @@ +{ + "IP": [ + { + "IP_Utilities": "v1.0.0" + } + ] +} \ No newline at end of file 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/23LC1024.v b/verify/uvm-python/23LC1024.v new file mode 100644 index 0000000..255d5e4 --- /dev/null +++ b/verify/uvm-python/23LC1024.v @@ -0,0 +1,612 @@ +// ******************************************************************************************************* +// ** ** +// ** 23LC1024.v - 23LC1024 1 MBIT SPI SERIAL SRAM (VCC = +2.5V TO +5.5V) ** +// ** ** +// ******************************************************************************************************* +// ** ** +// ** This information is distributed under license from Young Engineering. ** +// ** COPYRIGHT (c) 2014 YOUNG ENGINEERING ** +// ** ALL RIGHTS RESERVED ** +// ** ** +// ** ** +// ** Young Engineering provides design expertise for the digital world ** +// ** Started in 1990, Young Engineering offers products and services for your electronic design ** +// ** project. We have the expertise in PCB, FPGA, ASIC, firmware, and software design. ** +// ** From concept to prototype to production, we can help you. ** +// ** ** +// ** http://www.young-engineering.com/ ** +// ** ** +// ******************************************************************************************************* +// ** ** +// ** This information is provided to you for your convenience and use with Microchip products only. ** +// ** Microchip disclaims all liability arising from this information and its use. ** +// ** ** +// ** THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF ** +// ** ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO ** +// ** THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, ** +// ** PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE. ** +// ** MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL ** +// ** DAMAGES, FOR ANY REASON WHATSOEVER. ** +// ** ** +// ** It is your responsibility to ensure that your application meets with your specifications. ** +// ** ** +// ******************************************************************************************************* +// ** ** +// ** Revision : 1.0 ** +// ** Modified Date : 04/23/2014 ** +// ** Revision History: ** +// ** ** +// ** 04/23/2014: Initial design ** +// ** ** +// ******************************************************************************************************* +// ** TABLE OF CONTENTS ** +// ******************************************************************************************************* +// **---------------------------------------------------------------------------------------------------** +// ** DECLARATIONS ** +// **---------------------------------------------------------------------------------------------------** +// **---------------------------------------------------------------------------------------------------** +// ** INITIALIZATION ** +// **---------------------------------------------------------------------------------------------------** +// **---------------------------------------------------------------------------------------------------** +// ** CORE LOGIC ** +// **---------------------------------------------------------------------------------------------------** +// ** 1.01: Internal Reset Logic ** +// ** 1.02: Input Data Shifter ** +// ** 1.03: Clock Cycle Counter ** +// ** 1.04: Instruction Register ** +// ** 1.05: Address Register ** +// ** 1.06: Status Register Write ** +// ** 1.07: I/O Mode Instructions ** +// ** 1.08: Array Write ** +// ** 1.09: Output Data Shifter ** +// ** 1.10: Output Data Buffer ** +// ** ** +// **---------------------------------------------------------------------------------------------------** +// ** DEBUG LOGIC ** +// **---------------------------------------------------------------------------------------------------** +// ** 2.01: Memory Data Bytes ** +// ** ** +// **---------------------------------------------------------------------------------------------------** +// ** TIMING CHECKS ** +// **---------------------------------------------------------------------------------------------------** +// ** ** +// ******************************************************************************************************* + + +`timescale 1ns/10ps + +module M23LC1024 (SI_SIO0, SO_SIO1, SCK, CS_N, SIO2, HOLD_N_SIO3, RESET); + + inout SI_SIO0; // serial data input/output + input SCK; // serial data clock + + input CS_N; // chip select - active low + + inout SIO2; // serial data input/output + + inout HOLD_N_SIO3; // interface suspend - active low/ + // serial data input/output + + input RESET; // model reset/power-on reset + + inout SO_SIO1; // serial data input/output + + +// ******************************************************************************************************* +// ** DECLARATIONS ** +// ******************************************************************************************************* + + reg [07:00] DataShifterI; // serial input data shifter + reg [07:00] DataShifterO; // serial output data shifter + reg [31:00] ClockCounter; // serial input clock counter + reg [07:00] InstRegister; // instruction register + reg [16:00] AddrRegister; // address register + + wire InstructionREAD; // decoded instruction byte + wire InstructionRDMR; // decoded instruction byte + wire InstructionWRMR; // decoded instruction byte + wire InstructionWRITE; // decoded instruction byte + wire InstructionEDIO; // decoded instruction byte + wire InstructionEQIO; // decoded instruction byte + wire InstructionRSTIO; // decoded instruction byte + + reg [01:00] OpMode; // operation mode + + reg [01:00] IOMode; // I/O mode + + wire Hold; // hold function + + reg [07:00] MemoryBlock [0:131071]; // SRAM data memory array (131072x8) + + reg [03:00] SO_DO; // serial output data - data + wire SO_OE; // serial output data - output enable + + reg SO_Enable; // serial data output enable + + wire OutputEnable1; // timing accurate output enable + wire OutputEnable2; // timing accurate output enable + wire OutputEnable3; // timing accurate output enable + + integer tV; // timing parameter + integer tHZ; // timing parameter + integer tHV; // timing parameter + integer tDIS; // timing parameter + +`define READ 8'b0000_0011 // Read instruction +`define WRMR 8'b0000_0001 // Write Mode Register instruction +`define WRITE 8'b0000_0010 // Write instruction +`define RDMR 8'b0000_0101 // Read Mode Register instruction +`define EDIO 8'b0011_1011 // Enter Dual I/O instruction +`define EQIO 8'b0011_1000 // Enter Quad I/O instruction +`define RSTIO 8'b1111_1111 // Reset Dual and Quad I/O instruction + +`define BYTEMODE 2'b00 // Byte operation mode +`define PAGEMODE 2'b10 // Page operation mode +`define SEQMODE 2'b01 // Sequential operation mode + +`define SPIMODE 2'b00 // SPI I/O mode +`define SDIMODE 2'b01 // SDI I/O mode +`define SQIMODE 2'b10 // SQI I/O mode + +// ******************************************************************************************************* +// ** INITIALIZATION ** +// ******************************************************************************************************* + + initial begin + `ifdef TEMP_INDUSTRIAL + tV = 25; // output valid from SCK low + tHZ = 10; // HOLD_N low to output high-z + tHV = 50; // HOLD_N high to output valid + tDIS = 20; // CS_N high to output disable + `else + `ifdef TEMP_EXTENDED + tV = 32; // output valid from SCK low + tHZ = 10; // HOLD_N low to output high-z + tHV = 50; // HOLD_N high to output valid + tDIS = 20; // CS_N high to output disable + `else + tV = 25; // output valid from SCK low + tHZ = 10; // HOLD_N low to output high-z + tHV = 50; // HOLD_N high to output valid + tDIS = 20; // CS_N high to output disable + `endif + `endif + end + + initial begin + OpMode = `SEQMODE; + + IOMode = `SPIMODE; + end + + assign Hold = (HOLD_N_SIO3 == 0) & (IOMode == `SPIMODE); + + +// ******************************************************************************************************* +// ** CORE LOGIC ** +// ******************************************************************************************************* +// ------------------------------------------------------------------------------------------------------- +// 1.01: Internal Reset Logic +// ------------------------------------------------------------------------------------------------------- + + always @(negedge CS_N) ClockCounter <= 0; + always @(negedge CS_N) SO_Enable <= 0; + +// ------------------------------------------------------------------------------------------------------- +// 1.02: Input Data Shifter +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + if (Hold == 0) begin + if (CS_N == 0) begin + case (IOMode) + `SPIMODE: DataShifterI <= {DataShifterI[06:00],SI_SIO0}; + `SDIMODE: DataShifterI <= {DataShifterI[05:00],SO_SIO1,SI_SIO0}; + `SQIMODE: DataShifterI <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0}; + default: $error("IOMode set to invalid value."); + endcase + end + end + end + +// ------------------------------------------------------------------------------------------------------- +// 1.03: Clock Cycle Counter +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + if (Hold == 0) begin + if (CS_N == 0) ClockCounter <= ClockCounter + 1; + end + end + +// ------------------------------------------------------------------------------------------------------- +// 1.04: Instruction Register +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + if (Hold == 0) begin + case (IOMode) + `SPIMODE: begin + if (ClockCounter == 7) InstRegister <= {DataShifterI[06:00],SI_SIO0}; + end + `SDIMODE: begin + if (ClockCounter == 3) InstRegister <= {DataShifterI[05:00],SO_SIO1,SI_SIO0}; + end + `SQIMODE: begin + if (ClockCounter == 1) InstRegister <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0}; + end + default: $error("IOMode set to invalid value."); + endcase + end + end + + assign InstructionREAD = (InstRegister[7:0] == `READ); + assign InstructionRDMR = (InstRegister[7:0] == `RDMR); + assign InstructionWRMR = (InstRegister[7:0] == `WRMR); + assign InstructionWRITE = (InstRegister[7:0] == `WRITE); + assign InstructionEDIO = (InstRegister[7:0] == `EDIO); + assign InstructionEQIO = (InstRegister[7:0] == `EQIO); + assign InstructionRSTIO = (InstRegister[7:0] == `RSTIO); + +// ------------------------------------------------------------------------------------------------------- +// 1.05: Address Register +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + if (Hold == 0 & (InstructionREAD | InstructionWRITE)) begin + case (IOMode) + `SPIMODE: begin + if (ClockCounter == 15) AddrRegister[16] <= SI_SIO0; + else if (ClockCounter == 23) AddrRegister[15:08] <= {DataShifterI[06:00],SI_SIO0}; + else if (ClockCounter == 31) AddrRegister[07:00] <= {DataShifterI[06:00],SI_SIO0}; + end + `SDIMODE: begin + if (ClockCounter == 7) AddrRegister[16] <= SI_SIO0; + else if (ClockCounter == 11) AddrRegister[15:08] <= {DataShifterI[05:00],SO_SIO1,SI_SIO0}; + else if (ClockCounter == 15) AddrRegister[07:00] <= {DataShifterI[05:00],SO_SIO1,SI_SIO0}; + end + `SQIMODE: begin + if (ClockCounter == 3) AddrRegister[16] <= SI_SIO0; + else if (ClockCounter == 5) AddrRegister[15:08] <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0}; + else if (ClockCounter == 7) AddrRegister[07:00] <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0}; + end + default: $error("IOMode set to invalid value."); + endcase + end + end + +// ------------------------------------------------------------------------------------------------------- +// 1.06: Status Register Write +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + if (Hold == 0 & InstructionWRMR) begin + case (IOMode) + `SPIMODE: begin + if (ClockCounter == 15) OpMode <= DataShifterI[06:05]; + end + `SDIMODE: begin + if (ClockCounter == 7) OpMode <= DataShifterI[05:04]; + end + `SQIMODE: begin + if (ClockCounter == 3) OpMode <= DataShifterI[03:02]; + end + default: $error("IOMode set to invalid value."); + endcase + end + end + +// ------------------------------------------------------------------------------------------------------- +// 1.07: I/O Mode Instructions +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + case (IOMode) + `SPIMODE: begin + if (ClockCounter == 7) begin + if ({DataShifterI[06:00],SI_SIO0} == `EDIO) IOMode <= `SDIMODE; + else if ({DataShifterI[06:00],SI_SIO0} == `EQIO) IOMode <= `SQIMODE; + end + end + `SDIMODE: begin + if (ClockCounter == 3) begin + if ({DataShifterI[05:00],SO_SIO1,SI_SIO0} == `EQIO) IOMode <= `SQIMODE; + else if ({DataShifterI[05:00],SO_SIO1,SI_SIO0} == `RSTIO) IOMode <= `SPIMODE; + end + end + `SQIMODE: begin + if (ClockCounter == 1) begin + if ({DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0} == `EDIO) IOMode <= `SDIMODE; + else if ({DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0} == `RSTIO) IOMode <= `SPIMODE; + end + end + endcase + end + +// ------------------------------------------------------------------------------------------------------- +// 1.08: Array Write +// ------------------------------------------------------------------------------------------------------- + + always @(posedge SCK) begin + if (Hold == 0 & InstructionWRITE) begin + case (IOMode) + `SPIMODE: begin + if ((ClockCounter >= 39) & (ClockCounter[2:0] == 3'b111)) begin + MemoryBlock[AddrRegister[16:00]] <= {DataShifterI[06:00],SI_SIO0}; + + case (OpMode) + `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1; + `SEQMODE: AddrRegister[16:00] <= AddrRegister[16:00] + 1; + endcase + end + end + `SDIMODE: begin + if ((ClockCounter >= 19) & (ClockCounter[1:0] == 2'b11)) begin + MemoryBlock[AddrRegister[16:00]] <= {DataShifterI[05:00],SO_SIO1,SI_SIO0}; + + case (OpMode) + `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1; + `SEQMODE: AddrRegister[16:00] <= AddrRegister[16:00] + 1; + endcase + end + end + `SQIMODE: begin + if ((ClockCounter >= 9) & (ClockCounter[0] == 1'b1)) begin + MemoryBlock[AddrRegister[16:00]] <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0}; + + case (OpMode) + `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1; + `SEQMODE: AddrRegister[16:00] <= AddrRegister[16:00] + 1; + endcase + end + end + default: $error("IOMode set to invalid value."); + endcase + end + end + +// ------------------------------------------------------------------------------------------------------- +// 1.09: Output Data Shifter +// ------------------------------------------------------------------------------------------------------- + + always @(negedge SCK) begin + if (Hold == 0) begin + if (InstructionREAD) begin + case (IOMode) + `SPIMODE: begin + if ((ClockCounter >= 32) & (ClockCounter[2:0] == 3'b000)) begin + DataShifterO <= MemoryBlock[AddrRegister[16:00]]; + SO_Enable <= 1; + + case (OpMode) + `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1; + `SEQMODE: AddrRegister[16:00] <= AddrRegister[16:00] + 1; + endcase + end + else DataShifterO <= DataShifterO << 1; + end + `SDIMODE: begin + if ((ClockCounter >= 20) & (ClockCounter[1:0] == 2'b00)) begin + DataShifterO <= MemoryBlock[AddrRegister[16:00]]; + SO_Enable <= 1; + + case (OpMode) + `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1; + `SEQMODE: AddrRegister[16:00] <= AddrRegister[16:00] + 1; + endcase + end + else DataShifterO <= DataShifterO << 2; + end + `SQIMODE: begin + if ((ClockCounter >= 10) & (ClockCounter[0] == 1'b0)) begin + DataShifterO <= MemoryBlock[AddrRegister[16:00]]; + SO_Enable <= 1; + + case (OpMode) + `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1; + `SEQMODE: AddrRegister[16:00] <= AddrRegister[16:00] + 1; + endcase + end + else DataShifterO <= DataShifterO << 4; + end + default: $error("IOMode set to invalid value."); + endcase + end + else if (InstructionRDMR) begin + case (IOMode) + `SPIMODE: begin + if ((ClockCounter > 7) & (ClockCounter[2:0] == 3'b000)) begin + DataShifterO <= {OpMode,6'b000000}; + SO_Enable <= 1; + end + else DataShifterO <= DataShifterO << 1; + end + `SDIMODE: begin + if ((ClockCounter > 3) & (ClockCounter[1:0] == 2'b00)) begin + DataShifterO <= {OpMode,6'b000000}; + SO_Enable <= 1; + end + else DataShifterO <= DataShifterO << 2; + end + `SQIMODE: begin + if ((ClockCounter > 1) & (ClockCounter[0] == 1'b0)) begin + DataShifterO <= {OpMode,6'b000000}; + SO_Enable <= 1; + end + else DataShifterO <= DataShifterO << 4; + end + default: $error("IOMode set to invalid value."); + endcase + end + end + end + +// ------------------------------------------------------------------------------------------------------- +// 1.10: Output Data Buffer +// ------------------------------------------------------------------------------------------------------- + + // Buffer for SPI mode + bufif1 (SO_SIO1, SO_DO[0], SO_OE & (IOMode == `SPIMODE)); + + // Buffers for SDI mode + bufif1 (SI_SIO0, SO_DO[0], SO_OE & (IOMode == `SDIMODE)); + bufif1 (SO_SIO1, SO_DO[1], SO_OE & (IOMode == `SDIMODE)); + + // Buffers for SQI Mode + bufif1 (SI_SIO0, SO_DO[0], SO_OE & (IOMode == `SQIMODE)); + bufif1 (SO_SIO1, SO_DO[1], SO_OE & (IOMode == `SQIMODE)); + bufif1 (SIO2, SO_DO[2], SO_OE & (IOMode == `SQIMODE)); + bufif1 (HOLD_N_SIO3, SO_DO[3], SO_OE & (IOMode == `SQIMODE)); + + always @(DataShifterO) begin + case (IOMode) + `SPIMODE: begin + SO_DO[0] <= #(tV) DataShifterO[07]; + end + `SDIMODE: begin + SO_DO[1] <= #(tV) DataShifterO[07]; + SO_DO[0] <= #(tV) DataShifterO[06]; + end + `SQIMODE: begin + SO_DO[3] <= #(tV) DataShifterO[07]; + SO_DO[2] <= #(tV) DataShifterO[06]; + SO_DO[1] <= #(tV) DataShifterO[05]; + SO_DO[0] <= #(tV) DataShifterO[04]; + end + endcase + end + + bufif1 #(tV,0) (OutputEnable1, SO_Enable, 1); + notif1 #(tDIS) (OutputEnable2, CS_N, 1); + bufif1 #(tHV,tHZ) (OutputEnable3, HOLD_N_SIO3 | !(IOMode == `SPIMODE), 1); + + assign SO_OE = OutputEnable1 & OutputEnable2 & OutputEnable3; + + +// ******************************************************************************************************* +// ** DEBUG LOGIC ** +// ******************************************************************************************************* +// ------------------------------------------------------------------------------------------------------- +// 2.01: Memory Data Bytes +// ------------------------------------------------------------------------------------------------------- + + wire [07:00] MemoryByte00000 = MemoryBlock[000000]; + wire [07:00] MemoryByte00001 = MemoryBlock[000001]; + wire [07:00] MemoryByte00002 = MemoryBlock[000002]; + wire [07:00] MemoryByte00003 = MemoryBlock[000003]; + wire [07:00] MemoryByte00004 = MemoryBlock[000004]; + wire [07:00] MemoryByte00005 = MemoryBlock[000005]; + wire [07:00] MemoryByte00006 = MemoryBlock[000006]; + wire [07:00] MemoryByte00007 = MemoryBlock[000007]; + wire [07:00] MemoryByte00008 = MemoryBlock[000008]; + wire [07:00] MemoryByte00009 = MemoryBlock[000009]; + wire [07:00] MemoryByte0000A = MemoryBlock[000010]; + wire [07:00] MemoryByte0000B = MemoryBlock[000011]; + wire [07:00] MemoryByte0000C = MemoryBlock[000012]; + wire [07:00] MemoryByte0000D = MemoryBlock[000013]; + wire [07:00] MemoryByte0000E = MemoryBlock[000014]; + wire [07:00] MemoryByte0000F = MemoryBlock[000015]; + + wire [07:00] MemoryByte1FFF0 = MemoryBlock[131056]; + wire [07:00] MemoryByte1FFF1 = MemoryBlock[131057]; + wire [07:00] MemoryByte1FFF2 = MemoryBlock[131058]; + wire [07:00] MemoryByte1FFF3 = MemoryBlock[131059]; + wire [07:00] MemoryByte1FFF4 = MemoryBlock[131060]; + wire [07:00] MemoryByte1FFF5 = MemoryBlock[131061]; + wire [07:00] MemoryByte1FFF6 = MemoryBlock[131062]; + wire [07:00] MemoryByte1FFF7 = MemoryBlock[131063]; + wire [07:00] MemoryByte1FFF8 = MemoryBlock[131064]; + wire [07:00] MemoryByte1FFF9 = MemoryBlock[131065]; + wire [07:00] MemoryByte1FFFA = MemoryBlock[131066]; + wire [07:00] MemoryByte1FFFB = MemoryBlock[131067]; + wire [07:00] MemoryByte1FFFC = MemoryBlock[131068]; + wire [07:00] MemoryByte1FFFD = MemoryBlock[131069]; + wire [07:00] MemoryByte1FFFE = MemoryBlock[131070]; + wire [07:00] MemoryByte1FFFF = MemoryBlock[131071]; + +// ******************************************************************************************************* +// ** TIMING CHECKS ** +// ******************************************************************************************************* + + wire TimingCheckEnable = (RESET == 0) & (CS_N == 0); + wire SPITimingCheckEnable = TimingCheckEnable & (IOMode == `SPIMODE); + wire SDITimingCheckEnable = TimingCheckEnable & (IOMode == `SDIMODE) & (SO_Enable == 0); + wire SQITimingCheckEnable = TimingCheckEnable & (IOMode == `SQIMODE) & (SO_Enable == 0); + + specify + `ifdef TEMP_INDUSTRIAL + specparam + tHI = 25, // Clock high time + tLO = 25, // Clock low time + tSU = 10, // Data setup time + tHD = 10, // Data hold time + tHS = 10, // HOLD_N setup time + tHH = 10, // HOLD_N hold time + tCSD = 25, // CS_N disable time + tCSS = 25, // CS_N setup time + tCSH = 50, // CS_N hold time + tCLD = 25; // Clock delay time + `else + `ifdef TEMP_EXTENDED + specparam + tHI = 32, // Clock high time + tLO = 32, // Clock low time + tSU = 10, // Data setup time + tHD = 10, // Data hold time + tHS = 10, // HOLD_N setup time + tHH = 10, // HOLD_N hold time + tCSD = 32, // CS_N disable time + tCSS = 32, // CS_N setup time + tCSH = 50, // CS_N hold time + tCLD = 32; // Clock delay time + `else + specparam + tHI = 25, // Clock high time + tLO = 25, // Clock low time + tSU = 10, // Data setup time + tHD = 10, // Data hold time + tHS = 10, // HOLD_N setup time + tHH = 10, // HOLD_N hold time + tCSD = 25, // CS_N disable time + tCSS = 25, // CS_N setup time + tCSH = 50, // CS_N hold time + tCLD = 25; // Clock delay time + `endif + `endif + + $width (posedge SCK, tHI); + $width (negedge SCK, tLO); + $width (posedge CS_N, tCSD); + + $setup (negedge CS_N, posedge SCK &&& TimingCheckEnable, tCSS); + $setup (posedge CS_N, posedge SCK &&& TimingCheckEnable, tCLD); + + $hold (posedge SCK &&& TimingCheckEnable, posedge CS_N, tCSH); + + // SPI-specific timing checks + $setup (SI_SIO0, posedge SCK &&& SPITimingCheckEnable, tSU); + $setup (negedge SCK, negedge HOLD_N_SIO3 &&& SPITimingCheckEnable, tHS); + + $hold (posedge SCK &&& SPITimingCheckEnable, SI_SIO0, tHD); + $hold (posedge HOLD_N_SIO3 &&& SPITimingCheckEnable, posedge SCK, tHH); + + // SDI-specific timing checks + $setup (SI_SIO0, posedge SCK &&& SDITimingCheckEnable, tSU); + $setup (SO_SIO1, posedge SCK &&& SDITimingCheckEnable, tSU); + + $hold (posedge SCK &&& SDITimingCheckEnable, SI_SIO0, tHD); + $hold (posedge SCK &&& SDITimingCheckEnable, SO_SIO1, tHD); + + // SQI-specific timing checks + $setup (SI_SIO0, posedge SCK &&& SQITimingCheckEnable, tSU); + $setup (SO_SIO1, posedge SCK &&& SQITimingCheckEnable, tSU); + $setup (SIO2, posedge SCK &&& SQITimingCheckEnable, tSU); + $setup (HOLD_N_SIO3, posedge SCK &&& SQITimingCheckEnable, tSU); + + $hold (posedge SCK &&& SQITimingCheckEnable, SI_SIO0, tHD); + $hold (posedge SCK &&& SQITimingCheckEnable, SO_SIO1, tHD); + $hold (posedge SCK &&& SQITimingCheckEnable, SIO2, tHD); + $hold (posedge SCK &&& SQITimingCheckEnable, HOLD_N_SIO3, tHD); + endspecify + +endmodule diff --git a/verify/uvm-python/Makefile b/verify/uvm-python/Makefile new file mode 100644 index 0000000..0944ad3 --- /dev/null +++ b/verify/uvm-python/Makefile @@ -0,0 +1,52 @@ +PLUSARGS += "+UVM_VERBOSITY=UVM_MEDUIM" +TOPLEVEL := top +MODULE ?= top_module +AHB_FILES ?= $(PWD)/../../hdl/rtl/bus_wrapper/EF_PSRAM_CTRL_V2_ahbl.v +APB_FILES ?= # TODO: Add add APB wrapper file path +WB_FILES ?= # TODO: Add add WB wrapper file path +HDL_FILES ?= $(PWD)/../../hdl/rtl/EF_PSRAM_CTRL_V2.v +VIP_FILES ?= $(PWD)/23LC1024.v +VERILOG_SOURCES ?= $(PWD)/top.v $(AHB_FILES) $(HDL_FILES) $(VIP_FILES) +RTL_MACROS += "" # Add macros needed +BUS_TYPE ?= AHB +RTL_MACROS += -DBUS_TYPE_AHB +DESIGN_NAME = EF_PSRAM_CTRL_V2_ahbl +export CLK_MAKEFILE = HCLK +export RST_MAKEFILE = HRESETn +## netlist Gen +GL_MACROS += -DGL -DFUNCTIONAL $(RTL_MACROS) -DUNIT_DELAY=\#1 +PRE_SYS_FILES = $(AHB_FILES) $(APB_FILES) $(WB_FILES) $(HDL_FILES) +PDK_DIR = $(HOME)/.volare/volare/sky130/versions/bdc9412b3e468c102d01b7cf6337be06ec6e9c9a/sky130A/ +PDK_FILES = $(PDK_DIR)/libs.ref/sky130_fd_sc_hd/verilog/primitives.v $(PDK_DIR)/libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v # get this from openlane logs in the future +IPM_DIR = $(HOME)/.ipm +POST_SYS_FILES = $(PWD)/top.v $(VIP_FILES) $(PWD)/../../hdl/gl/synthesis/nl/$(DESIGN_NAME).nl.v + +# ifeq ($(BUS_TYPE),APB) +# RTL_MACROS += -DBUS_TYPE_APB +# else ifeq ($(BUS_TYPE),AHB) +# RTL_MACROS += -DBUS_TYPE_AHB +# else ifeq ($(BUS_TYPE),WISHBONE) +# RTL_MACROS += -DBUS_TYPE_WISHBONE +# endif +# RTL_MACROS ?= "-DSKIP_WAVE_DUMP" +YAML_FILE = $(PWD)/../../EF_PSRAM_CTRL_V2.yaml # TODO: update yaml file path +MAKEFLAGS += --no-print-directory + +# List of tests +TESTS := psram_spi_test psram_sqi_test psram_sdi_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/psram_agent/psram_driver.py b/verify/uvm-python/psram_agent/psram_driver.py new file mode 100644 index 0000000..2a51358 --- /dev/null +++ b/verify/uvm-python/psram_agent/psram_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 psram_driver(ip_driver): + def __init__(self, name="psram_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(psram_driver) diff --git a/verify/uvm-python/psram_agent/psram_monitor.py b/verify/uvm-python/psram_agent/psram_monitor.py new file mode 100644 index 0000000..6685475 --- /dev/null +++ b/verify/uvm-python/psram_agent/psram_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 psram_monitor(ip_monitor): + def __init__(self, name="psram_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(psram_monitor) diff --git a/verify/uvm-python/psram_bus_item.py b/verify/uvm-python/psram_bus_item.py new file mode 100644 index 0000000..279ea6d --- /dev/null +++ b/verify/uvm-python/psram_bus_item.py @@ -0,0 +1,14 @@ +from EF_UVM.bus_env.bus_item import bus_item +from uvm.macros import ( + uvm_object_utils_begin, + uvm_object_utils_end, + uvm_field_int, + uvm_object_utils, +) + +class psram_bus_item(bus_item): + def __init__(self, name="psram_bus_item"): + super().__init__(name) + self.rand("addr", range(0, 0xFFFFFFF)) + +uvm_object_utils(psram_bus_item) diff --git a/verify/uvm-python/psram_coverage/psram_coverage.py b/verify/uvm-python/psram_coverage/psram_coverage.py new file mode 100644 index 0000000..f80e7e9 --- /dev/null +++ b/verify/uvm-python/psram_coverage/psram_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 psram_coverage(ip_coverage): + """ + component that initialize the coverage groups and control when to sample the data. + """ + + def __init__(self, name="psram_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(psram_coverage) diff --git a/verify/uvm-python/psram_interface/psram_if.py b/verify/uvm-python/psram_interface/psram_if.py new file mode 100644 index 0000000..2e0e35d --- /dev/null +++ b/verify/uvm-python/psram_interface/psram_if.py @@ -0,0 +1,9 @@ +from uvm.base.sv import sv_if + + +class psram_if(sv_if): + def __init__(self, dut): + # TODO: Add signal need to be seen by the ip monitor and driver in the following format + # bus_map = {: } + bus_map = {"mode": "IOMode"} + super().__init__(dut, "", bus_map) diff --git a/verify/uvm-python/psram_item/psram_item.py b/verify/uvm-python/psram_item/psram_item.py new file mode 100644 index 0000000..6f1eda0 --- /dev/null +++ b/verify/uvm-python/psram_item/psram_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 psram_item(ip_item): + def __init__(self, name="psram_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(psram_item) diff --git a/verify/uvm-python/psram_logger/psram_logger.py b/verify/uvm-python/psram_logger/psram_logger.py new file mode 100644 index 0000000..390a1a5 --- /dev/null +++ b/verify/uvm-python/psram_logger/psram_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 psram_logger(ip_logger): + def __init__(self, name="psram_logger", parent=None): + super().__init__(name, parent) + return + uvm_fatal("psram_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(psram_logger) diff --git a/verify/uvm-python/psram_ref_model/psram_ref_model.py b/verify/uvm-python/psram_ref_model/psram_ref_model.py new file mode 100644 index 0000000..5caa8c0 --- /dev/null +++ b/verify/uvm-python/psram_ref_model/psram_ref_model.py @@ -0,0 +1,157 @@ +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 psram_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="psram_ref_model", parent=None): + super().__init__(name, parent) + self.tag = name + self.ris_reg = 0 + self.mis_reg = 0 + self.irq = 0 + self.mis_changed = Event() + self.icr_changed = Event() + self.external_mem = Memory(size=0x1FFFF) # the slave is 1 MB + + 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) + # Here add the log to run when simulation starts + + 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: + # TODO: write logic needed when write transaction is received + # For example, to write the same value to the same resgiter uncomment the following lines + if tr.addr & 0xFFF00000 == 0x00700000: # access to the external memory + addr = tr.addr & 0xFFFFF + if tr.size == bus_item.WORD_ACCESS: + self.external_mem.write_word(addr, tr.data) + elif tr.size == bus_item.HALF_WORD_ACCESS: + self.external_mem.write_halfword(addr, tr.data) + elif tr.size == bus_item.BYTE_ACCESS: + self.external_mem.write_byte(addr, tr.data) + else: + self.regs.write_reg_value(tr.addr, tr.data) + self.bus_bus_export.write(tr) # this is output to the scoreboard + + # check if the write register is icr , set the icr changed event + elif tr.kind == bus_item.READ: + # TODO: write logic needed when read transaction is received + # For example, to read the same resgiter uncomment the following lines + td = tr.do_clone() + if tr.addr & 0xFFF00000 == 0x00700000: # access to the external memory + addr = tr.addr & 0xFFFFF + if tr.size == bus_item.WORD_ACCESS: + td.data = self.external_mem.read_word(addr) + elif tr.size == bus_item.HALF_WORD_ACCESS: + td.data = self.external_mem.read_halfword(addr) + elif tr.size == bus_item.BYTE_ACCESS: + td.data = self.external_mem.read_byte(addr) + else: + data = self.regs.read_reg_value(tr.addr, mask=0xFFFFFF) + td.data = data + 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, + ) + + # Update interrupts when a new ip transaction is received + self.set_ris_reg() + # Here the ref model should predict the transaction and send it to scoreboard + # self.ip_export.write(td) # this is output ro scoreboard + + +uvm_component_utils(psram_ref_model) + +class Memory: + def __init__(self, size=0x00700000): + """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/psram_seq_lib/psram_base_seq.py b/verify/uvm-python/psram_seq_lib/psram_base_seq.py new file mode 100644 index 0000000..12df0a1 --- /dev/null +++ b/verify/uvm-python/psram_seq_lib/psram_base_seq.py @@ -0,0 +1,149 @@ +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 psram_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="psram_bus_seq"): + super().__init__(name) + self.valid_addr = set() + + async def body(self): + await super().body() + arr = [] + if not UVMConfigDb.get(self, "", "bus_regs", arr): + uvm_fatal(self.tag, "No json file wrapper regs") + else: + self.regs = arr[0].reg_name_to_address + + 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, 0x1FFFF) + self.req.addr = 0x00700000 + 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, 0x1FFFF) + self.req.addr = 0x00700000 + address + self.req.data = 0 + self.req.kind = bus_item.READ + self.req.size = 2 if size is None else size + await uvm_do(self, self.req) + + async def write_bulk(self, address, bulk_size, access_size): + for _ in range(bulk_size): + await self.write_to_mem(address=address, size=access_size) + address += 4 if access_size == bus_item.WORD_ACCESS else 2 if access_size == bus_item.HALF_WORD_ACCESS else 1 + + async def read_bulk(self, address, bulk_size, access_size): + for _ in range(bulk_size): + if not self.valid_read(address, access_size): + return + await self.read_from_mem(address=address, size=access_size) + address += 4 if access_size == bus_item.WORD_ACCESS else 2 if access_size == bus_item.HALF_WORD_ACCESS else 1 + + def valid_read(self, address, access_size): + if address not in self.valid_addr: + return False + if access_size == bus_item.WORD_ACCESS: + return address in self.valid_addr and address + 3 in self.valid_addr + elif access_size == bus_item.HALF_WORD_ACCESS: + return address in self.valid_addr and address + 1 in self.valid_addr + else: + return address in self.valid_addr + + async def set_mode(self, mode): + if mode not in ["SPI", "SDI", "SQI"]: + uvm_fatal(self.tag, f"Invalid mode {mode} should be SPI, SDI or SQI") + if mode == "SQI": + await self.set_waitstates(2) + await self.psram_send_req(is_write=True, reg="eqpi_cmd", data=0x38) + await self.psram_send_req(is_write=True, reg="enter_qpi", data=1) + await self.psram_send_req(is_write=True, reg=0x0, data=0x0) + for _ in range(10): + await self.send_nop() + await self.psram_send_req(is_write=True, reg="enter_qpi", data=0) + await self.psram_send_req(is_write=True, reg="mode", data=2) + elif mode == "SDI": + await self.set_waitstates(2) + await self.psram_send_req(is_write=True, reg="eqpi_cmd", data=0x3B) + await self.psram_send_req(is_write=True, reg="enter_qpi", data=1) + await self.psram_send_req(is_write=True, reg=0x0, data=0x0) + for _ in range(10): + await self.send_nop() + await self.psram_send_req(is_write=True, reg="enter_qpi", data=0) + await self.psram_send_req(is_write=True, reg="mode", data=1) + + async def psram_send_req(self, is_write, reg, data): + self.create_new_item() + self.req.rand_mode(0) + self.req.addr = reg if type(reg) is int else self.regs[reg] + self.req.data = data + self.req.kind = bus_item.WRITE + await uvm_do(self, self.req) + + async def set_waitstates(self, waitstates): + await self.psram_send_req(is_write=True, reg="wait_states", data=waitstates) + + async def write_multiple(self): + for i in range(random.randint(10, 30)): + await self.access_rand(is_write=True) + + async def access_rand(self, is_write=True): + if is_write: + address = random.randint(0, 0x1FFFF // 4) * 4 + else: + filtered_set = {x for x in self.valid_addr if x % 4 == 0} + address = random.choice(list(self.valid_addr)) + bulk_size = random.randint(1, 10) + access_size = random.choice([bus_item.WORD_ACCESS, bus_item.HALF_WORD_ACCESS, bus_item.BYTE_ACCESS]) + if is_write == False: # correct read size depend on the address + if access_size == bus_item.BYTE_ACCESS: + pass + elif access_size == bus_item.HALF_WORD_ACCESS : + if address % 2 != 0: + access_size = bus_item.BYTE_ACCESS + elif access_size == bus_item.WORD_ACCESS: + if address % 4 != 0: + access_size = bus_item.BYTE_ACCESS + last_address = address + (bulk_size - 1) * (4 if access_size == bus_item.WORD_ACCESS else 2 if access_size == bus_item.HALF_WORD_ACCESS else 1) + if last_address > 0x1FFFF: + bulk_size = 1 + if is_write: + await self.write_bulk(address=address, bulk_size=bulk_size, access_size=access_size) + else: + await self.read_bulk(address=address, bulk_size=bulk_size, access_size=access_size) + + async def write_read_multiple(self): + for i in range(random.randint(10, 30)): + if random.random() < 0.8: # read 80% of the time + await self.access_rand(is_write=False) + else: + await self.access_rand(is_write=True) + + +uvm_object_utils(psram_base_seq) diff --git a/verify/uvm-python/psram_seq_lib/psram_ip_seq.py b/verify/uvm-python/psram_seq_lib/psram_ip_seq.py new file mode 100644 index 0000000..11a27d3 --- /dev/null +++ b/verify/uvm-python/psram_seq_lib/psram_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 psram_item.psram_item import psram_item +from uvm.seq import UVMSequence + + +class psram_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="psram_ip_seq"): + UVMSequence.__init__(self, name) + self.set_automatic_phase_objection(1) + self.req = psram_item() + self.rsp = psram_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 psram_var1: psram_var1 == 10, lambda psram_var2: psram_var2 > 7, ......) + # send item without conditions + # await uvm_do(self, self.req) + pass + + +uvm_object_utils(psram_ip_seq) diff --git a/verify/uvm-python/psram_seq_lib/psram_sdi_seq.py b/verify/uvm-python/psram_seq_lib/psram_sdi_seq.py new file mode 100644 index 0000000..b0b8005 --- /dev/null +++ b/verify/uvm-python/psram_seq_lib/psram_sdi_seq.py @@ -0,0 +1,30 @@ +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 psram_seq_lib.psram_base_seq import psram_base_seq + + +class psram_sdi_seq(psram_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="psram_sdi_seq"): + super().__init__(name) + + async def body(self): + await super().body() + await self.set_mode("SDI") + # await self.write_bulk(address=0, bulk_size=2, access_size=bus_item.WORD_ACCESS) + # await self.read_bulk(address=0, bulk_size=2, access_size=bus_item.WORD_ACCESS) + await self.write_multiple() + for _ in range(30): + await self.write_read_multiple() + + +uvm_object_utils(psram_sdi_seq) diff --git a/verify/uvm-python/psram_seq_lib/psram_spi_seq.py b/verify/uvm-python/psram_seq_lib/psram_spi_seq.py new file mode 100644 index 0000000..c0d26a4 --- /dev/null +++ b/verify/uvm-python/psram_seq_lib/psram_spi_seq.py @@ -0,0 +1,27 @@ +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 psram_seq_lib.psram_base_seq import psram_base_seq + + +class psram_spi_seq(psram_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="psram_bus_seq"): + super().__init__(name) + + async def body(self): + await super().body() + await self.write_multiple() + for _ in range(30): + await self.write_read_multiple() + + +uvm_object_utils(psram_spi_seq) diff --git a/verify/uvm-python/psram_seq_lib/psram_sqi_seq.py b/verify/uvm-python/psram_seq_lib/psram_sqi_seq.py new file mode 100644 index 0000000..5d74727 --- /dev/null +++ b/verify/uvm-python/psram_seq_lib/psram_sqi_seq.py @@ -0,0 +1,30 @@ +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 psram_seq_lib.psram_base_seq import psram_base_seq + + +class psram_sqi_seq(psram_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="psram_sqi_seq"): + super().__init__(name) + + async def body(self): + await super().body() + await self.set_mode("SQI") + # await self.write_bulk(address=0, bulk_size=2, access_size=bus_item.WORD_ACCESS) + # await self.read_bulk(address=0, bulk_size=2, access_size=bus_item.WORD_ACCESS) + await self.write_multiple() + for _ in range(30): + await self.write_read_multiple() + + +uvm_object_utils(psram_sqi_seq) diff --git a/verify/uvm-python/test_lib.py b/verify/uvm-python/test_lib.py new file mode 100644 index 0000000..0d749e1 --- /dev/null +++ b/verify/uvm-python/test_lib.py @@ -0,0 +1,195 @@ +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 psram_interface.psram_if import psram_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 psram_seq_lib.psram_ip_seq import psram_ip_seq + +# override classes +from EF_UVM.ip_env.ip_agent.ip_driver import ip_driver +from psram_agent.psram_driver import psram_driver +from EF_UVM.ip_env.ip_agent.ip_monitor import ip_monitor +from psram_agent.psram_monitor import psram_monitor +from EF_UVM.ref_model.ref_model import ref_model +from psram_ref_model.psram_ref_model import psram_ref_model +from EF_UVM.ip_env.ip_coverage.ip_coverage import ip_coverage +from psram_coverage.psram_coverage import psram_coverage +from EF_UVM.ip_env.ip_logger.ip_logger import ip_logger +from psram_logger.psram_logger import psram_logger + +# override but_item to change size +from EF_UVM.bus_env.bus_item import bus_item +from psram_bus_item import psram_bus_item + + +# +from psram_seq_lib.psram_spi_seq import psram_spi_seq +from psram_seq_lib.psram_sqi_seq import psram_sqi_seq +from psram_seq_lib.psram_sdi_seq import psram_sdi_seq + +@cocotb.test() +async def module_top(dut): + # profiler = cProfile.Profile() + # profiler.enable() + BUS_TYPE = cocotb.plusargs["BUS_TYPE"] + pif = psram_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 psram_base_test(base_test): + def __init__(self, name="psram_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(), psram_driver.get_type()) + self.set_type_override_by_type( + ip_monitor.get_type(), psram_monitor.get_type() + ) + self.set_type_override_by_type( + ref_model.get_type(), psram_ref_model.get_type() + ) + self.set_type_override_by_type( + ip_coverage.get_type(), psram_coverage.get_type() + ) + self.set_type_override_by_type(ip_logger.get_type(), psram_logger.get_type()) + self.set_type_override_by_type(bus_item.get_type(), psram_bus_item.get_type()) + + def end_of_elaboration_phase(self, phase): + super().end_of_elaboration_phase(phase) + self.ip_interface = [] + if not UVMConfigDb.get(self, "", "ip_if", self.ip_interface): + uvm_fatal(self.tag, "No interface specified for self driver instance") + else: + self.ip_interface = self.ip_interface[0] + + def check_mode(self, expected): + if self.ip_interface.mode != expected: + uvm_fatal("check_mode", f"Expected mode {expected} but got {self.ip_interface.mode}") + else: + uvm_info("check_mode", f"Mode is {expected}", UVM_LOW) + + # async def post_reset_phase(self, phase): + # await super().post_reset_phase(phase) + # bus_seq = psram_enable_clock() + # await bus_seq.start(self.bus_sqr) + # return super().post_reset_phase(phase) + + +uvm_component_utils(psram_base_test) + + +class psram_spi_test(psram_base_test): + def __init__(self, name="psram__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 = psram_spi_seq("psram_spi_seq") + # ip_seq = psram_ip_seq("psram_ip_seq") + await bus_seq.start(self.bus_sqr) + # await ip_seq.start(self.ip_sqr) + phase.drop_objection(self, f"{self.__class__.__name__} drop objection") + + async def shutdown_phase(self, phase): + await super().shutdown_phase(phase) + self.check_mode(0) # SPI + + +uvm_component_utils(psram_spi_test) + + +class psram_sqi_test(psram_base_test): + def __init__(self, name="psram__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 = psram_sqi_seq("psram_sqi_seq") + # ip_seq = psram_ip_seq("psram_ip_seq") + await bus_seq.start(self.bus_sqr) + # await ip_seq.start(self.ip_sqr) + phase.drop_objection(self, f"{self.__class__.__name__} drop objection") + + async def shutdown_phase(self, phase): + await super().shutdown_phase(phase) + self.check_mode(2) # SQI + + +uvm_component_utils(psram_sqi_test) + + +class psram_sdi_test(psram_base_test): + def __init__(self, name="psram__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 = psram_sdi_seq("psram_sdi_seq") + # ip_seq = psram_ip_seq("psram_ip_seq") + await bus_seq.start(self.bus_sqr) + # await ip_seq.start(self.ip_sqr) + phase.drop_objection(self, f"{self.__class__.__name__} drop objection") + + async def shutdown_phase(self, phase): + await super().shutdown_phase(phase) + self.check_mode(1) # SDI + + +uvm_component_utils(psram_sdi_test) + diff --git a/verify/uvm-python/top.v b/verify/uvm-python/top.v new file mode 100644 index 0000000..b9699d3 --- /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; + + wire [3:0] din; + wire [3:0] dout; + wire [3:0] douten; + + wire sck; + wire ce_n; + wire sio0 = douten[0] ? dout[0] : 1'bz; + wire sio1 = douten[1] ? dout[1] : 1'bz; + wire sio2 = douten[2] ? dout[2] : 1'bz; + wire sio3 = douten[3] ? dout[3] : 1'bz; + pullup(sio3); // Attach a weak pull-up to sio3 for hold pin + + assign din = {sio3, sio2, sio1, sio0}; + + `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 + EF_PSRAM_CTRL_V2_ahbl dut(.HCLK(CLK), .HRESETn(RESETn), .HADDR(HADDR), .HWRITE(HWRITE), .HSEL(HSEL), .HSIZE(HSIZE), .HTRANS(HTRANS), .HWDATA(HWDATA), .HRDATA(HRDATA), .HREADY(HREADY),.HREADYOUT(HREADYOUT), .sck(sck), .ce_n(ce_n), .din(din), .dout(dout), .douten(douten)); + `endif // BUS_TYPE_AHB + M23LC1024 slave( + .SCK(sck), + .CS_N(ce_n), + .SI_SIO0(sio0), + .SO_SIO1(sio1), + .SIO2(sio2), + .HOLD_N_SIO3(sio3), + .RESET(~RESETn) + ); + wire [1:0] IOMode = slave.IOMode; + // 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 212a84a7df16f9cd0978452e4c15b486dbda72bf Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Wed, 16 Oct 2024 10:21:14 +0300 Subject: [PATCH 2/2] Add the CI --- .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