diff --git a/hw/top_chip/dv/env/seq_lib/top_chip_dv_gpio_base_vseq.sv b/hw/top_chip/dv/env/seq_lib/top_chip_dv_gpio_base_vseq.sv new file mode 100644 index 000000000..f8e100d63 --- /dev/null +++ b/hw/top_chip/dv/env/seq_lib/top_chip_dv_gpio_base_vseq.sv @@ -0,0 +1,125 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class top_chip_dv_gpio_base_vseq extends top_chip_dv_base_vseq; + `uvm_object_utils(top_chip_dv_gpio_base_vseq) + + // Standard SV/UVM methods + extern function new(string name=""); + extern task body(); + + // Class specific methods + // + // Waits for the pattern to appear on the GPIOs + extern virtual task wait_for_pattern(logic [NUM_GPIOS-1:0] exp_val); + + // Drives a pattern on the quarter GPIOs pins in input mode + extern virtual task drive_pattern(int unsigned wait_num_clks, + int unsigned pins_starting_quarter, + int unsigned pins_next_quarter, + bit state); +endclass : top_chip_dv_gpio_base_vseq + +function top_chip_dv_gpio_base_vseq::new (string name = ""); + super.new(name); +endfunction : new + +task top_chip_dv_gpio_base_vseq::body(); + super.body(); + `DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest); + + // Checks the GPIOs in both input and output mode. SW writes walking 0's and walking 1's pattern + // on the first and third quarter of direct_out register and body() waits (for a reasonable + // amount of timeout) for that pattern to appear on the pads. Then it drives all 1's and all 0's + // on the other even quaters. + // + // Enable the pulldowns so that the pads are driving 0's rather than Z's when no external driver + // is connected + cfg.gpio_vif.set_pulldown_en('1); + + // Current Current GPIOs pads state : 'h0 + + `uvm_info(`gfn, "Starting GPIOs outputs test", UVM_LOW) + + // Check for walking 1's pattern on the first quarter of pins. + for (int i = 0; i < NUM_GPIOS/4; i++) begin + wait_for_pattern({24'h0, 1 << i}); + end + + // Current GPIOs pads state : 'h00000080 + // + // Drive all 1's on the second quarter of pins. + drive_pattern(2, 1, 2, 1); + + // Current GPIOs pads state : 'h0000FF80 + // + // Check for walking 1's pattern on the third quarter of pins. + for (int i = 0; i < NUM_GPIOS/4; i++) begin + wait_for_pattern({8'h0, 1 << i, 16'hFF80}); + end + + // Current GPIOs pads state : 'h0080FF80 + // + // Drive all 1's on the fourth quarter of pins. + drive_pattern(2, 3, 4, 1); + + // Current GPIOs pads state : 'hFF80FF80 + // + // The SW first sets the first and third quarter of GPIOs to 1's in order to walk 0's on them. + // The second and fourth quarter of pads should contain all 1's by now. + // + // Wait and check for all 1s. + wait_for_pattern({NUM_GPIOS{1'b1}}); + + // Current GPIOs pads state : 'hFFFFFFFF + // + // Check for walking 0's pattern on the first quarter of pins. + for (int i = 0; i < NUM_GPIOS/4; i++) begin + wait_for_pattern({24'hFF_FFFF, ~(1 << i)}); + end + + // Current GPIOs pads state : 'hFFFFFF7F + // + // Drive all 0's on the second quarter of pins. + drive_pattern(2, 1, 2, 0); + + // Current GPIOs pads state : 'hFFFF007F + // + // Check for walking 0's pattern on on the third quarter of pins. + for (int i = 0; i < NUM_GPIOS/4; i++) begin + wait_for_pattern({8'hFF, ~(1 << i), 16'h007F}); + end + + // Current GPIOs pads state : 'hFF7F007F + // + // Drive all 0's on the fourth quarter of pins. + drive_pattern(2, 3, 4, 0); + + // Current GPIOs pads state : 'h007F007F +endtask : body + +task top_chip_dv_gpio_base_vseq::wait_for_pattern(logic [NUM_GPIOS-1:0] exp_val); + `DV_SPINWAIT(wait(cfg.gpio_vif.pins === exp_val);, + $sformatf("Timed out waiting for GPIOs == %0h", exp_val), + /*use default_spinwait_timeout_ns*/, + `gfn) +endtask : wait_for_pattern + +task top_chip_dv_gpio_base_vseq::drive_pattern(int unsigned wait_num_clks, + int unsigned pins_starting_quarter, + int unsigned pins_next_quarter, + bit state); + + int unsigned quarter_start = (NUM_GPIOS/4) * pins_starting_quarter; + int unsigned quarter_end = (NUM_GPIOS/4) * pins_next_quarter; + + // Wait for some cycles so that the pads can transition from the current pin state to the next pin + // state + cfg.sys_clk_vif.wait_clks(wait_num_clks); + + // Drive state on the quarter of pins + for (int i = quarter_start; i < quarter_end; i++) begin + cfg.gpio_vif.drive_pin(i, state); + end +endtask : drive_pattern diff --git a/hw/top_chip/dv/env/seq_lib/top_chip_dv_vseq_list.sv b/hw/top_chip/dv/env/seq_lib/top_chip_dv_vseq_list.sv index a81201afd..9d47c898c 100644 --- a/hw/top_chip/dv/env/seq_lib/top_chip_dv_vseq_list.sv +++ b/hw/top_chip/dv/env/seq_lib/top_chip_dv_vseq_list.sv @@ -4,3 +4,4 @@ `include "top_chip_dv_base_vseq.sv" `include "top_chip_dv_uart_base_vseq.sv" +`include "top_chip_dv_gpio_base_vseq.sv" diff --git a/hw/top_chip/dv/env/top_chip_dv_env.core b/hw/top_chip/dv/env/top_chip_dv_env.core index 26dddaeaf..cbcf13f0c 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env.core +++ b/hw/top_chip/dv/env/top_chip_dv_env.core @@ -12,6 +12,7 @@ filesets: - lowrisc:dv:mem_bkdr_util - lowrisc:dv:uart_agent - lowrisc:dv:common_ifs + - lowrisc:mocha_dv:gpio_env files: - top_chip_dv_env_pkg.sv - mem_clear_util.sv: {is_include_file: true} @@ -22,6 +23,7 @@ filesets: - seq_lib/top_chip_dv_vseq_list.sv: {is_include_file: true} - seq_lib/top_chip_dv_base_vseq.sv: {is_include_file: true} - seq_lib/top_chip_dv_uart_base_vseq.sv: {is_include_file: true} + - seq_lib/top_chip_dv_gpio_base_vseq.sv: {is_include_file: true} file_type: systemVerilogSource targets: diff --git a/hw/top_chip/dv/env/top_chip_dv_env.sv b/hw/top_chip/dv/env/top_chip_dv_env.sv index 096fd6853..730a35cf4 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env.sv +++ b/hw/top_chip/dv/env/top_chip_dv_env.sv @@ -44,6 +44,11 @@ function void top_chip_dv_env::build_phase(uvm_phase phase); `uvm_fatal(`gfn, "failed to get sw_logger_vif from uvm_config_db") end + // Get the GPIO VIF handle + if (!uvm_config_db#(virtual pins_if #(NUM_GPIOS))::get(this, "", "gpio_vif", cfg.gpio_vif)) begin + `uvm_fatal(`gfn, "Failed to retrieve gpio_vif from uvm_config_db") + end + // Initialize the sw logger interface. foreach (cfg.mem_image_files[i]) begin if (i inside {ChipMemSRAM}) begin diff --git a/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv b/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv index 47beeb1b6..f0b3fd7f6 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv +++ b/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv @@ -9,6 +9,10 @@ class top_chip_dv_env_cfg extends uvm_object; // External interfaces virtual clk_rst_if sys_clk_vif; virtual clk_rst_if peri_clk_vif; + + // GPIO Pads interface + virtual pins_if #(NUM_GPIOS) gpio_vif; + // Software logging & status interfaces virtual sw_logger_if sw_logger_vif; virtual sw_test_status_if sw_test_status_vif; diff --git a/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv b/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv index e956b0a09..c73b3da54 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv +++ b/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv @@ -9,6 +9,7 @@ package top_chip_dv_env_pkg; import mem_bkdr_util_pkg::*; import sw_test_status_pkg::*; import uart_agent_pkg::*; + import gpio_env_pkg::NUM_GPIOS; // Macro includes `include "uvm_macros.svh" diff --git a/hw/top_chip/dv/tb/tb.sv b/hw/top_chip/dv/tb/tb.sv index 12c25dbc2..93eb20403 100644 --- a/hw/top_chip/dv/tb/tb.sv +++ b/hw/top_chip/dv/tb/tb.sv @@ -10,6 +10,7 @@ module tb; import mem_bkdr_util_pkg::mem_bkdr_util; import top_chip_dv_env_pkg::*; import top_chip_dv_test_pkg::*; + import gpio_env_pkg::NUM_GPIOS; import top_chip_dv_env_pkg::SW_DV_START_ADDR; import top_chip_dv_env_pkg::SW_DV_TEST_STATUS_ADDR; @@ -25,11 +26,15 @@ module tb; wire rst_n; wire peri_clk; wire peri_rst_n; + wire [NUM_GPIOS-1:0] gpio_pads; // A wire connected to bidirectional pads in pins_if + logic [NUM_GPIOS-1:0] dut_gpio_o; + logic [NUM_GPIOS-1:0] dut_gpio_en_o; // ------ Interfaces ------ clk_rst_if sys_clk_if(.clk(clk), .rst_n(rst_n)); clk_rst_if peri_clk_if(.clk(peri_clk), .rst_n(peri_rst_n)); uart_if uart_if(); + pins_if #(NUM_GPIOS) gpio_if (.pins(gpio_pads)); // ------ Mock DRAM ------ top_pkg::axi_dram_req_t dram_req; @@ -49,6 +54,10 @@ module tb; // Clock and reset. .clk_i (clk ), .rst_ni (rst_n ), + // GPIO inputs and outputs with output enable + .gpio_i (gpio_pads ), + .gpio_o (dut_gpio_o ), + .gpio_en_o (dut_gpio_en_o ), // UART receive and transmit. .uart_rx_i (uart_if.uart_rx ), .uart_tx_o (uart_if.uart_tx ), @@ -69,6 +78,12 @@ module tb; .dram_resp_i (dram_resp ) ); + // Assignment to the GPIO pads. If dut_gpio_en_o[i] is disabled, the let the gpio_pad[i] floating + // so an external device/driver can drive it. + for (genvar i = 0; i < NUM_GPIOS; i++) begin : gen_gpio_pads + assign gpio_pads[i] = dut_gpio_en_o[i] ? dut_gpio_o[i] : 1'bz; + end + // Signals to connect the sink top_pkg::axi_req_t sim_sram_cpu_req; top_pkg::axi_resp_t sim_sram_cpu_resp; @@ -180,6 +195,7 @@ module tb; uvm_config_db#(virtual clk_rst_if)::set(null, "*", "sys_clk_if", sys_clk_if); uvm_config_db#(virtual clk_rst_if)::set(null, "*", "peri_clk_if", peri_clk_if); uvm_config_db#(virtual uart_if)::set(null, "*.env.m_uart_agent*", "vif", uart_if); + uvm_config_db#(virtual pins_if #(NUM_GPIOS))::set(null, "*.env", "gpio_vif", gpio_if); // SW logger and test status interfaces. uvm_config_db#(virtual sw_test_status_if)::set( diff --git a/hw/top_chip/dv/top_chip_sim.core b/hw/top_chip/dv/top_chip_sim.core index 400dbaf69..3c192ed94 100644 --- a/hw/top_chip/dv/top_chip_sim.core +++ b/hw/top_chip/dv/top_chip_sim.core @@ -25,6 +25,8 @@ filesets: - lowrisc:dv:xbar_macros - lowrisc:dv_dpi_c:uartdpi:0.1 - lowrisc:dv_dpi_sv:uartdpi:0.1 + - lowrisc:mocha_dv:gpio_if:0.1 + - lowrisc:mocha_dv:gpio_env files: - tb/tb.sv - tb/chip_hier_macros.svh: {is_include_file: true} diff --git a/hw/top_chip/dv/top_chip_sim_cfg.hjson b/hw/top_chip/dv/top_chip_sim_cfg.hjson index 4c577b1eb..4eece373a 100644 --- a/hw/top_chip/dv/top_chip_sim_cfg.hjson +++ b/hw/top_chip/dv/top_chip_sim_cfg.hjson @@ -130,7 +130,7 @@ } { name: gpio_smoke - uvm_test_seq: top_chip_dv_gpio_vseq + uvm_test_seq: top_chip_dv_gpio_base_vseq sw_images: ["gpio_smoketest_vanilla_bare:5"] run_opts: ["+ChipMemSRAM_image_file={run_dir}/gpio_smoketest_vanilla_bare.vmem"] } diff --git a/signals.svwf b/signals.svwf new file mode 100644 index 000000000..82c5132ce --- /dev/null +++ b/signals.svwf @@ -0,0 +1,36 @@ + +# +# Mnemonic Maps +# +mmap new -reuse -name {Boolean as Logic} -radix %b -contents {{%c=FALSE -edgepriority 1 -shape low} +{%c=TRUE -edgepriority 1 -shape high}} +mmap new -reuse -name {Example Map} -radix %x -contents {{%b=11???? -bgcolor orange -label REG:%x -linecolor yellow -shape bus} +{%x=1F -bgcolor red -label ERROR -linecolor white -shape EVENT} +{%x=2C -bgcolor red -label ERROR -linecolor white -shape EVENT} +{%x=* -label %x -linecolor gray -shape bus}} + +array unset createdGroup +array set createdGroup {} +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.clk_i}]} + } ]] +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.rst_ni}]} + } ]] +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.cio_gpio_i[31:0]}]} + } ]] +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.cio_gpio_en_o[31:0]}]} + } ]] +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.cio_gpio_o[31:0]}]} + } ]] +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.tl_i}]} + } ]] +set id [waveform add -signals [subst { + {[format {tb.dut.u_gpio.tl_o}]} + } ]] + +waveform xview limits 0 501700.823ns diff --git a/sw/device/lib/hal/gpio.c b/sw/device/lib/hal/gpio.c index 44f9dd8e1..bf4450efb 100644 --- a/sw/device/lib/hal/gpio.c +++ b/sw/device/lib/hal/gpio.c @@ -30,3 +30,13 @@ void gpio_set_oe_pin(gpio_t gpio, uint32_t pin, bool output) uint32_t mask = 1u << (pin & 0xFu); DEV_WRITE(gpio + reg, (mask << 16) | (output ? mask : 0u)); } + +void gpio_write(gpio_t gpio, uint32_t output_reg_addr, uint32_t value) +{ + DEV_WRITE(gpio + output_reg_addr, value); +} + +void gpio_set_all_oe(gpio_t gpio, uint32_t value) +{ + DEV_WRITE(gpio + GPIO_REG_DIRECT_OE, value); +} diff --git a/sw/device/lib/hal/gpio.h b/sw/device/lib/hal/gpio.h index e1511d93e..17ce9a828 100644 --- a/sw/device/lib/hal/gpio.h +++ b/sw/device/lib/hal/gpio.h @@ -25,5 +25,11 @@ bool gpio_read_pin(gpio_t gpio, uint32_t pin); // Set the value of one output pin void gpio_write_pin(gpio_t gpio, uint32_t pin, bool state); +// Set the value of all output pins +void gpio_write(gpio_t gpio, uint32_t output_reg_addr, uint32_t value); + // Set the output enable state of one pin void gpio_set_oe_pin(gpio_t gpio, uint32_t pin, bool output); + +// Set the output enable state of all pins +void gpio_set_all_oe(gpio_t gpio, uint32_t value); diff --git a/sw/device/tests/gpio/smoketest.c b/sw/device/tests/gpio/smoketest.c index b0f72a9d7..e1960bf45 100644 --- a/sw/device/tests/gpio/smoketest.c +++ b/sw/device/tests/gpio/smoketest.c @@ -8,49 +8,102 @@ #include #include -// Check that we can write and read some GPIO registers -static bool reg_test(gpio_t gpio) +// Drive a pattern (val) +static void drive(gpio_t gpio, uint32_t masked_reg, uint32_t val) { - uint32_t hal_val; + gpio_write(gpio, masked_reg, 0XFFFF0000 | val); +} - // Inputs - hal_val = 0; - for (int ii = 0; ii < GPIO_NUM_PINS; ii++) { - hal_val |= (gpio_read_pin(gpio, ii) << ii); - } - if (hal_val != DEV_READ(gpio + GPIO_REG_DATA_IN)) { - return false; +// Wait for an expected pattern (compare_val). +static void wait(gpio_t gpio, uint32_t compare_val) +{ + while (DEV_READ(gpio + GPIO_REG_DATA_IN) != compare_val) { } +} + +// Verifies GPIOs in partially output and input direction. The test distributes GPIOs as four equal +// quarters. The idea is to drive first quarter of GPIOs as outputs and wait for a pattern to appear +// on the second quarter of pins as inputs. Next, drive a pattern on the third quarter and waits for a +// pattern to appear on the fourth quarter as inputs. Repeat the same process second time but with a +// different pattern. +// +// The pattern driven on the outputs is going to be walking 1's (1, 10, 0100, 1000, ...) first and +// then walking 0's (1110, 1101, 1011, 0111, ...) whereas it is all 1's then all 0's sequence for +// the inputs. +// +// 1- Walk 1's on the first quarter of GPIOs in output mode. +// 2- top_chip_dv_gpio_base_vseq will wait for walking 1's pattern to appear on the pads. Once it +// sees that pattern, it will drive all 1's on to the second quarter. +// 4- gpio_test waits for the pattern 0x0000FF80 on the GPIO pads by reading DATA_IN register. Then +// it will walk 1's on the third quarter of pins and waits for pattern 0xFF80FF80. +// 5- On the other side, the vseq waits for the walking 1's pattern on the third quarter of pins and +// drive all 1's on the fourth quarter. +// 6- After all that, gpio_test start to write 1's to the first and third quarter of pins in order to +// drive walking 0's. Everything beyond that is similar but the expected driven sequence is going +// to be 0's on the inputs and walking 0's on the outputs. +static bool gpio_test(gpio_t gpio) +{ + // Enable the first and third quarter of pins in output mode + gpio_set_all_oe(gpio, 0x00FF00FF); - // Outputs - hal_val = 0xC1A0; // Ciao! - for (int nn = 0; nn < 2; nn++) { - for (int ii = 0; ii < GPIO_NUM_PINS; ii++) { - gpio_write_pin(gpio, ii, ((hal_val >> ii) & 0x1)); + // Current GPIOs pads state : 0x00000000 + // + // Walk 1's on the first quarter. vseq drives the second quarter with all 1's. Hence, the + // expected value to wait for is 0xFF80, + for (int i = 0; i < GPIO_NUM_PINS / 4; i++) { + drive(gpio, GPIO_REG_MASKED_OUT_LOWER, 1 << i); + if (i == ((GPIO_NUM_PINS / 4) - 1)) { + wait(gpio, 0xFF80); } - if (hal_val != DEV_READ(gpio + GPIO_REG_DIRECT_OUT)) { - return false; + } + + // Current GPIOs pads state : 0x0000FF80 + // + // Walk 1's on the third quarter. vseq drives the fourth quarter with all 1's. Additionally, the + // pads contains 0xFF80 by now on the first two quarters. Hence, the expected value to wait for + // is 0xFF80FF80, + for (int i = 0; i < GPIO_NUM_PINS / 4; i++) { + drive(gpio, GPIO_REG_MASKED_OUT_UPPER, 1 << i); + if (i == ((GPIO_NUM_PINS / 4) - 1)) { + wait(gpio, 0xFF80FF80); } - hal_val = ~hal_val; // invert to check for constant bits } - // Output enables - hal_val = 0xB7EE; // Byee! - for (int nn = 0; nn < 2; nn++) { - for (int ii = 0; ii < GPIO_NUM_PINS; ii++) { - gpio_set_oe_pin(gpio, ii, ((hal_val >> ii) & 0x1)); + // Current GPIOs pads state : 0xFF80FF80 + // + // Now, set the first and third quarter (which are enabled as outputs) to all 1's in order to + // walk 0's on them. + gpio_write(gpio, GPIO_REG_DIRECT_OUT, 0x00FF00FF); + + // Current GPIOs pads state : 0xFFFFFFFF + // + // Walk 0's on the first quarter of pins. vseq drives the second quarter with all 0's. + // Hence, the expected value to wait for is 0xFFFF007F. + for (int i = 0; i < GPIO_NUM_PINS / 4; i++) { + drive(gpio, GPIO_REG_MASKED_OUT_LOWER, ~(1 << i)); + if (i == ((GPIO_NUM_PINS / 4) - 1)) { + wait(gpio, 0xFFFF007F); } - if (hal_val != DEV_READ(gpio + GPIO_REG_DIRECT_OE)) { - return false; + } + + // Current GPIOs pads state : 0xFFFF007F + // + // Walk 0's on the third quarter of pins. vseq drives the fourth quarter with all 0's. + // Hence, the expected value to wait for is 0x007F007F. + for (int i = 0; i < GPIO_NUM_PINS / 4; i++) { + drive(gpio, GPIO_REG_MASKED_OUT_UPPER, ~(1 << i)); + if (i == ((GPIO_NUM_PINS / 4) - 1)) { + wait(gpio, 0x007F007F); } - hal_val = ~hal_val; // invert to check for constant bits } + // Current GPIOs pads state : 0x007F007F + return true; } bool test_main() { gpio_t gpio = mocha_system_gpio(); - return reg_test(gpio); + return gpio_test(gpio); }