Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions hw/top_chip/dv/env/seq_lib/top_chip_dv_i2c_host_tx_rx_vseq.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright lowRISC contributors (Sunburst project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// Stimulus vseq for testing the I2C Controller-mode module at a chip level
//
// - The SW test will program the DUT-I2C block into host mode, and then configure a
// write-transfer with some randomly-generated data.
// - The I2C_Agent is configured to run an 'autoresponder' sequence (i2c_device_response_seq),
// which accepts all write transfers, and returns the same data in read transfers.
// - SW will configure a read-transfer, and check it reads back the same data as was written as
// part of the write-transfer.
//
// The logic in this vseq is mostly to configure the i2c_agent with an appropriate set of timing
// parameters, and then to start the autoresponder sequence.
//
class top_chip_dv_i2c_host_tx_rx_vseq extends top_chip_dv_i2c_tx_rx_vseq;

///////////////
// VARIABLES //
///////////////

rand int i2c_idx;

`uvm_object_utils_begin(top_chip_dv_i2c_host_tx_rx_vseq)
`uvm_field_int(i2c_idx, UVM_DEFAULT | UVM_DEC)
`uvm_object_utils_end

`uvm_object_new

/////////////////
// CONSTRAINTS //
/////////////////

constraint i2c_idx_c {
i2c_idx inside {[0 : NI2cs-1]};
}

/////////////
// METHODS //
/////////////

virtual function void configure_agent_timing_cfg();
// tClockLow needs to be "slightly" shorter than the actual clock low period
// After fixing #15003, the clock low needs to be shortened by 1 additional cycle
// to account for delayed output. The ClockLow value is not used to program
// the DUT, but instead is used by the i2c agent to simulate when it should begin
// driving data. The i2c agent drives data as late as possible.
int tSetupBit = 2;
// Nullify the CDC instrumentation delays on the input synchronizers,
// since SDA never truly changes simultaneously with SCL. Their happening
// on the same cycle in simulation is due to time/cycle quantization.
// Drive SDA a cycle early to account for CDC delays, if CDC is enabled.
// Hold SDA a cycle longer to account for CDC delays, if CDC is enabled.
if (p_sequencer.cfg.en_dv_cdc) begin
tSetupBit++;
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tHoldBit = 1;
end
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSetupBit = tSetupBit;
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockLow = half_period_cycles - tSetupBit;
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockPulse = half_period_cycles;
endfunction : configure_agent_timing_cfg

task pre_start();
void'($value$plusargs("i2c_idx=%0d", i2c_idx));
`DV_CHECK_FATAL(i2c_idx inside {[0 : NI2cs-1]})
super.pre_start();
endtask

virtual task body();
bit[7:0] clock_period_nanos_arr[1] = {clock_period_nanos};
bit[7:0] rise_fall_nanos_arr[1] = {rise_fall_nanos};
bit[7:0] i2c_clock_period_nanos_arr[4] = {<<byte{i2c_clock_period_nanos}};
bit[7:0] i2c_idx_arr[1];
bit[7:0] i2c_cdc_enabled_arr[1];
super.body();

i2c_idx_arr = {i2c_idx};

// Pass symbols to test software via the backdoor to configure the DUT via SW.
// - The I2C peripheral instance to test is selected via the plusarg '+i2c_idx'
// - The timing parameters are taken from static values inherited from 'chip_sw_i2c_tx_rx_vseq'
sw_symbol_backdoor_overwrite("kClockPeriodNanos", clock_period_nanos_arr);
sw_symbol_backdoor_overwrite("kI2cRiseFallNanos", rise_fall_nanos_arr);
sw_symbol_backdoor_overwrite("kI2cClockPeriodNanos", i2c_clock_period_nanos_arr);
sw_symbol_backdoor_overwrite("kI2cIdx", i2c_idx_arr);
if (p_sequencer.cfg.en_dv_cdc) begin
i2c_cdc_enabled_arr = {8'd1};
sw_symbol_backdoor_overwrite("kI2cCdcInstrumentationEnabled", i2c_cdc_enabled_arr);
end

// Enable the appropriate i2c_monitor
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].en_monitor = 1'b1;
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].if_mode = Device;

// Enable the interface
// Sunburst: We always use weak pull-ups on the I^2C nets in the testbench
// as there is no pinmux, so there is nothing to enable here.

// Configure the I2C Agent with an appropriate set of timing parameters to drive the stimulus.
configure_agent_timing_cfg();

`uvm_info(`gfn, $sformatf("Test vseq properties:\n%s", this.sprint()), UVM_MEDIUM)
print_i2c_timing_cfg(i2c_idx);

// Start the i2c_agent sequence which accepts all write and read transactions, and returns the
// same data bytes when read that were previously written.
i2c_device_autoresponder();
endtask

virtual task i2c_device_autoresponder();
i2c_device_response_seq seq = i2c_device_response_seq::type_id::create("seq");
fork seq.start(p_sequencer.i2c_sequencer_hs[i2c_idx]); join_none
endtask

endclass : top_chip_dv_i2c_host_tx_rx_vseq
85 changes: 85 additions & 0 deletions hw/top_chip/dv/env/seq_lib/top_chip_dv_i2c_tx_rx_vseq.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright lowRISC contributors (Sunburst project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

class top_chip_dv_i2c_tx_rx_vseq extends top_chip_dv_base_vseq;

///////////////
// VARIABLES //
///////////////

// TODO(OpenTitan#23920) Figure out a better way to calculate this based on testbench clock frequency
int clock_period_nanos = 41;
int i2c_clock_period_nanos = 1000;
int rise_fall_nanos = 10;
int rise_cycles = ((rise_fall_nanos - 1) / clock_period_nanos) + 1;
int fall_cycles = ((rise_fall_nanos - 1) / clock_period_nanos) + 1;
int clock_period_cycles = ((i2c_clock_period_nanos - 1) / clock_period_nanos) + 1;
int half_period_cycles = ((i2c_clock_period_nanos/2 - 1) / clock_period_nanos) + 1;

// Keep a count of the total number of read data bytes we expect the agent to read from the DUT.
int sent_rd_byte[NI2cs] = '{NI2cs{ 0 }};

`uvm_object_utils_begin(top_chip_dv_i2c_tx_rx_vseq)
`uvm_field_int(clock_period_nanos, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(i2c_clock_period_nanos, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(rise_fall_nanos, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(rise_cycles, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(fall_cycles, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(clock_period_cycles, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(half_period_cycles, UVM_DEFAULT | UVM_DEC)
`uvm_object_utils_end

`uvm_object_new

/////////////
// METHODS //
/////////////

virtual task body();
super.body();

// Wait for reset to be asserted and de-asserted before trying to set
// config parameters and enable the monitor, otherwise the monitor may
// bork the sequence item already received by the sequence.
p_sequencer.ifs.peri_clk_if.wait_for_reset();
endtask

function void print_i2c_timing_cfg(uint i2c_idx);
string str;
str = {str, $sformatf("\n timing_cfg.tSetupStart : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSetupStart)};
str = {str, $sformatf("\n timing_cfg.tHoldStart : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tHoldStart)};
str = {str, $sformatf("\n timing_cfg.tClockStart : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockStart)};
str = {str, $sformatf("\n timing_cfg.tClockLow : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockLow)};
str = {str, $sformatf("\n timing_cfg.tSetupBit : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSetupBit)};
str = {str, $sformatf("\n timing_cfg.tClockPulse : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockPulse)};
str = {str, $sformatf("\n timing_cfg.tHoldBit : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tHoldBit)};
str = {str, $sformatf("\n timing_cfg.tClockStop : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockStop)};
str = {str, $sformatf("\n timing_cfg.tSetupStop : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSetupStop)};
str = {str, $sformatf("\n timing_cfg.tHoldStop : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tHoldStop)};
str = {str, $sformatf("\n timing_cfg.tTimeOut : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tTimeOut)};
str = {str, $sformatf("\n timing_cfg.enbTimeOut : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.enbTimeOut)};
str = {str, $sformatf("\n timing_cfg.tStretchHostClock : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tStretchHostClock)};
str = {str, $sformatf("\n timing_cfg.tSdaUnstable : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSdaUnstable)};
str = {str, $sformatf("\n timing_cfg.tSdaInterference : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSdaInterference)};
str = {str, $sformatf("\n timing_cfg.tSclInterference : %d",
p_sequencer.cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSclInterference)};
`uvm_info(`gfn, $sformatf("%s", str), UVM_MEDIUM);
endfunction

endclass : top_chip_dv_i2c_tx_rx_vseq
2 changes: 2 additions & 0 deletions hw/top_chip/dv/env/seq_lib/top_chip_dv_vseq_list.sv
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

`include "top_chip_dv_base_vseq.sv"
`include "top_chip_dv_example_vseq.sv"
`include "top_chip_dv_i2c_tx_rx_vseq.sv"
`include "top_chip_dv_i2c_host_tx_rx_vseq.sv"
`include "top_chip_dv_pattgen_vseq.sv"
`include "top_chip_dv_uart_base_vseq.sv"
`include "top_chip_dv_uart_tx_rx_vseq.sv"
Expand Down
4 changes: 4 additions & 0 deletions hw/top_chip/dv/env/top_chip_dv_env.core
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ filesets:
- lowrisc:dv:sw_test_status
- lowrisc:dv:sw_logger_if
- lowrisc:dv:mem_bkdr_util
- lowrisc:dv:i2c_agent
- lowrisc:dv:spi_agent
- lowrisc:dv:pattgen_agent
- lowrisc:dv:uart_agent
- lowrisc:dv:pins_if
Expand All @@ -27,6 +29,8 @@ 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_example_vseq.sv: {is_include_file: true}
- seq_lib/top_chip_dv_i2c_tx_rx_vseq.sv: {is_include_file: true}
- seq_lib/top_chip_dv_i2c_host_tx_rx_vseq.sv: {is_include_file: true}
- seq_lib/top_chip_dv_pattgen_vseq.sv: {is_include_file: true}
- seq_lib/top_chip_dv_uart_base_vseq.sv: {is_include_file: true}
- seq_lib/top_chip_dv_uart_tx_rx_vseq.sv: {is_include_file: true}
Expand Down
24 changes: 24 additions & 0 deletions hw/top_chip/dv/env/top_chip_dv_env.sv
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ class top_chip_dv_env extends uvm_env;
mem_bkdr_util mem_bkdr_util_h[chip_mem_e];

// Agents
i2c_agent m_i2c_agents[NI2cs];
pattgen_agent m_pattgen_agent;
spi_agent m_spi_device_agents[NSpis];
uart_agent m_uart_agents[NUarts];

function void build_phase(uvm_phase phase);
Expand Down Expand Up @@ -59,10 +61,23 @@ class top_chip_dv_env extends uvm_env;
`uvm_fatal(`gfn, "Cannot get gpio_pins_vif")
end

// Instantiate I^2C agents
foreach (m_i2c_agents[i]) begin
m_i2c_agents[i] = i2c_agent::type_id::create($sformatf("m_i2c_agent%0d", i), this);
uvm_config_db#(i2c_agent_cfg)::set(this, $sformatf("m_i2c_agent%0d*", i), "cfg",cfg.m_i2c_agent_cfgs[i]);
end

// Instantiate pattgen agent
m_pattgen_agent = pattgen_agent::type_id::create("m_pattgen_agent", this);
uvm_config_db#(pattgen_agent_cfg)::set(this, "m_pattgen_agent*", "cfg", cfg.m_pattgen_agent_cfg);

// Instantiate SPI device agents (for connecting to SPI hosts)
foreach (m_spi_device_agents[i]) begin
m_spi_device_agents[i] = spi_agent::type_id::create($sformatf("m_spi_device_agents%0d", i), this);
uvm_config_db#(spi_agent_cfg)::set(this, $sformatf("m_spi_device_agents%0d*", i), "cfg",
cfg.m_spi_device_agent_cfgs[i]);
end

// Instantiate uart agents
foreach (m_uart_agents[i]) begin
m_uart_agents[i] = uart_agent::type_id::create($sformatf("m_uart_agent%0d", i), this);
Expand All @@ -82,12 +97,21 @@ class top_chip_dv_env extends uvm_env;
super.connect_phase(phase);
// Track specific agent sequencers in the virtual sequencer.
// Allows virtual sequences to use the agents to drive RX items.
foreach (m_i2c_agents[i]) begin
virtual_sequencer.i2c_sequencer_hs[i] = m_i2c_agents[i].sequencer;
end
foreach (m_spi_device_agents[i]) begin
virtual_sequencer.spi_device_sequencer_hs[i] = m_spi_device_agents[i].sequencer;
end
foreach (m_uart_agents[i]) begin
virtual_sequencer.uart_sequencer_hs[i] = m_uart_agents[i].sequencer;
end

// Connect monitor outputs to matching FIFOs in the virtual sequencer.
// Allows virtual sequences to check TX items.
foreach (m_i2c_agents[i]) begin
m_i2c_agents[i].monitor.controller_mode_rd_item_port.connect(virtual_sequencer.i2c_rd_fifos[i].analysis_export);
end
for (int i = 0; i < NUM_PATTGEN_CHANNELS; i++) begin
m_pattgen_agent.monitor.item_port[i].connect(virtual_sequencer.pattgen_rx_fifo[i].analysis_export);
end
Expand Down
20 changes: 20 additions & 0 deletions hw/top_chip/dv/env/top_chip_dv_env_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ class top_chip_dv_env_cfg extends uvm_object;
string mem_image_files[chip_mem_e];
longint unsigned sys_timeout_cycles = 20_000_000;

// DV CDC enable - used in top_chip_dv_i2c_host_tx_rx_vseq.
// TODO: Add some way to control this? Randomise for now.
bit en_dv_cdc = $urandom_range(1,0);

// Software logging & status interfaces
virtual sw_logger_if sw_logger_vif;
virtual sw_test_status_if sw_test_status_vif;

// External interface agent configs
rand i2c_agent_cfg m_i2c_agent_cfgs[NI2cs];
rand spi_agent_cfg m_spi_device_agent_cfgs[NSpis];
rand pattgen_agent_cfg m_pattgen_agent_cfg;
rand uart_agent_cfg m_uart_agent_cfgs[NUarts];

Expand All @@ -24,12 +30,26 @@ class top_chip_dv_env_cfg extends uvm_object;
virtual function void initialize();
get_mem_image_files_from_plusargs();

// create i2c agent config obj
foreach (m_i2c_agent_cfgs[i]) begin
m_i2c_agent_cfgs[i] = i2c_agent_cfg::type_id::create($sformatf("m_i2c_agent_cfg%0d", i));
// Set default monitor enable to zero for shared io agents.
m_i2c_agent_cfgs[i].en_monitor = 1'b0;
end

// create pattgen agent config obj
m_pattgen_agent_cfg = pattgen_agent_cfg::type_id::create("m_pattgen_agent_cfg");
m_pattgen_agent_cfg.if_mode = Device;
// Configuration is required to perform meaningful monitoring
m_pattgen_agent_cfg.en_monitor = 0;

// create spi device agent config obj
foreach (m_spi_device_agent_cfgs[i]) begin
m_spi_device_agent_cfgs[i] = spi_agent_cfg::type_id::create($sformatf("m_spi_device_agent_cfg%0d", i));
// all the spi_agents talking to the host interface should be configured into device mode.
m_spi_device_agent_cfgs[i].if_mode = dv_utils_pkg::Device;
end

// create uart agent config obj
foreach (m_uart_agent_cfgs[i]) begin
m_uart_agent_cfgs[i] = uart_agent_cfg::type_id::create($sformatf("m_uart_agent_cfg%0d", i));
Expand Down
4 changes: 4 additions & 0 deletions hw/top_chip/dv/env/top_chip_dv_env_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package top_chip_dv_env_pkg;
import uvm_pkg::*;
import dv_utils_pkg::*;
import mem_bkdr_util_pkg::*;
import i2c_agent_pkg::*;
import spi_agent_pkg::*;
import pattgen_agent_pkg::*;
import uart_agent_pkg::*;

Expand All @@ -20,6 +22,8 @@ package top_chip_dv_env_pkg;
} chip_mem_e;

localparam int unsigned NGpioPins = 32;
localparam int unsigned NI2cs = 2;
localparam int unsigned NSpis = 2;
localparam int unsigned NUarts = 2;
localparam int unsigned UartDpiBaud = 1_500_000;

Expand Down
5 changes: 5 additions & 0 deletions hw/top_chip/dv/env/top_chip_dv_virtual_sequencer.sv
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@ class top_chip_dv_virtual_sequencer extends uvm_sequencer;

// Handles to specific interface agent sequencers. Used by some virtual
// sequences to drive RX (to-chip) items.
i2c_sequencer i2c_sequencer_hs[NI2cs];
spi_sequencer spi_device_sequencer_hs[NSpis];
uart_sequencer uart_sequencer_hs[NUarts];

// FIFOs for monitor output. Used by some virtual sequences to check
// TX (from-chip) items.
uvm_tlm_analysis_fifo #(i2c_item) i2c_rd_fifos[NI2cs];
uvm_tlm_analysis_fifo #(pattgen_item) pattgen_rx_fifo[NUM_PATTGEN_CHANNELS];
uvm_tlm_analysis_fifo #(uart_item) uart_tx_fifos[NUarts];

function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Construct monitor output FIFOs
foreach (i2c_rd_fifos[i]) i2c_rd_fifos[i] = new($sformatf("i2c_rd_fifo%0d", i), this);
foreach (pattgen_rx_fifo[i]) pattgen_rx_fifo[i] = new($sformatf("pattgen_rx_fifo%0d", i), this);
foreach (uart_tx_fifos[i]) uart_tx_fifos[i] = new($sformatf("uart_tx_fifo%0d", i), this);
endfunction
Expand Down
Loading