Skip to content
Draft
39 changes: 39 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,39 @@
// Copyright lowRISC contributors (COSMIC project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// This vseq is going to be starting a reactive sequence.
//
// i2c_monitor shares an analysis port with i2c_sequencer. It sends an i2c_item which contains
// a member "state". i2c_monitor watches the i2c_if and as soon as it sees the communication started
// on the bus, it change the state accordingly. Based on the state received on the
// analysis port of sequencer, i2c_base_seq initializes the start_item method to send i2c_item to
// i2c_driver so that it can drive ack, nack or rdata to the controller.
class top_chip_dv_i2c_host_tx_rx_vseq extends top_chip_dv_base_vseq;
`uvm_object_utils(top_chip_dv_i2c_host_tx_rx_vseq)

// Standard SV/UVM methods
extern function new(string name="");
extern task body();
endclass : top_chip_dv_i2c_host_tx_rx_vseq

function top_chip_dv_i2c_host_tx_rx_vseq::new(string name = "");
super.new(name);
endfunction : new

task top_chip_dv_i2c_host_tx_rx_vseq::body();
i2c_device_response_seq seq = i2c_device_response_seq::type_id::create("seq");

// Configure the agent to be reactive
cfg.m_i2c_agent_cfg.if_mode = Device;

super.body();

`DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest);

`uvm_info(`gfn, "Starting I2C Host TX-RX test", UVM_LOW)

fork
seq.start(p_sequencer.i2c_sqr);
join_none
endtask : body
1 change: 1 addition & 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 @@ -5,3 +5,4 @@
`include "top_chip_dv_base_vseq.sv"
`include "top_chip_dv_uart_base_vseq.sv"
`include "top_chip_dv_gpio_smoke_vseq.sv"
`include "top_chip_dv_i2c_host_tx_rx_vseq.sv"
2 changes: 2 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,7 @@ filesets:
- lowrisc:dv:uart_agent
- lowrisc:dv:common_ifs
- lowrisc:mocha_dv:gpio_env
- lowrisc:dv:i2c_env
files:
- top_chip_dv_env_pkg.sv
- mem_clear_util.sv: {is_include_file: true}
Expand All @@ -24,6 +25,7 @@ filesets:
- 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_smoke_vseq.sv: {is_include_file: true}
- seq_lib/top_chip_dv_i2c_host_tx_rx_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource

targets:
Expand Down
8 changes: 8 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 @@ -13,6 +13,7 @@ class top_chip_dv_env extends uvm_env;

// Agents
uart_agent m_uart_agent;
i2c_agent m_i2c_agent;

// Standard SV/UVM methods
extern function new(string name = "", uvm_component parent = null);
Expand Down Expand Up @@ -67,6 +68,12 @@ function void top_chip_dv_env::build_phase(uvm_phase phase);
`uvm_fatal(`gfn, "Cannot get sys_clk_vif")
end

// Set I2C agent config object for I2C agent
uvm_config_db#(i2c_agent_cfg)::set(this, "m_i2c_agent", "cfg", cfg.m_i2c_agent_cfg);

// Create I2C agent
m_i2c_agent = i2c_agent::type_id::create("m_i2c_agent", this);

// Instantiate UART agent
m_uart_agent = uart_agent::type_id::create("m_uart_agent", this);
uvm_config_db#(uart_agent_cfg)::set(this, "m_uart_agent*", "cfg", cfg.m_uart_agent_cfg);
Expand All @@ -83,6 +90,7 @@ function void top_chip_dv_env::connect_phase(uvm_phase phase);
// Track specific agent sequencers in the virtual sequencer.
// Allows virtual sequences to use the agents to drive RX items.
top_vsqr.uart_sqr = m_uart_agent.sequencer;
top_vsqr.i2c_sqr = m_i2c_agent.sequencer;

// Connect monitor output to matching FIFO in the virtual sequencer.
// Allows virtual sequences to check TX items.
Expand Down
4 changes: 4 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 @@ -18,6 +18,7 @@ class top_chip_dv_env_cfg extends uvm_object;

// External interface agent configs
rand uart_agent_cfg m_uart_agent_cfg;
i2c_agent_cfg m_i2c_agent_cfg;

`uvm_object_utils_begin(top_chip_dv_env_cfg)
`uvm_object_utils_end
Expand Down Expand Up @@ -45,6 +46,9 @@ function void top_chip_dv_env_cfg::initialize();
// Configuration is required to perform meaningful monitoring
m_uart_agent_cfg.en_tx_monitor = 0;
m_uart_agent_cfg.en_rx_monitor = 0;

// Create I2C agent config object
m_i2c_agent_cfg = i2c_agent_cfg::type_id::create("m_i2c_agent_cfg");
endfunction : initialize

function void top_chip_dv_env_cfg::get_mem_image_files_from_plusargs();
Expand Down
1 change: 1 addition & 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 @@ -14,6 +14,7 @@ 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.
uart_sequencer uart_sqr;
i2c_sequencer i2c_sqr;

// FIFOs for monitor output. Used by some virtual sequences to check
// TX (from-chip) items.
Expand Down
24 changes: 24 additions & 0 deletions hw/top_chip/dv/tb/tb.sv
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,22 @@ module tb;
logic [3:0] spi_host_sd;
logic [3:0] spi_host_sd_en;

// I2C connections
wire scl;
wire sda;
logic scl_en_o;
logic sda_en_o;
logic scl_o;
logic sda_o;

// ------ Interfaces ------
clk_rst_if sys_clk_if(.clk(clk), .rst_n(rst_n));
uart_if uart_if();
pins_if #(NUM_GPIOS) gpio_pins_if (.pins(gpio_pads));
i2c_if i2c_if (.clk_i(clk),
.rst_ni(rst_n),
.scl_io(scl),
.sda_io(sda));

// ------ Mock DRAM ------
top_pkg::axi_dram_req_t dram_req;
Expand Down Expand Up @@ -66,6 +78,13 @@ module tb;
// UART receive and transmit.
.uart_rx_i (uart_if.uart_rx ),
.uart_tx_o (uart_if.uart_tx ),
// I2C controller/target bidirectional interface.
.i2c_scl_i (scl ),
.i2c_scl_o (scl_o ),
.i2c_scl_en_o (scl_en_o ),
.i2c_sda_i (sda ),
.i2c_sda_o (sda_o ),
.i2c_sda_en_o (sda_en_o ),
// External Mailbox port
.axi_mailbox_req_i ('0 ),
.axi_mailbox_resp_o ( ),
Expand Down Expand Up @@ -106,6 +125,10 @@ module tb;
assign gpio_pads[i] = dut_gpio_en_o[i] ? dut_gpio_o[i] : 1'bz;
end

// Modelling the open-drain circuit
assign (strong0, weak1) scl = (scl_en_o) ? scl_o : 1'b1;
assign (strong0, weak1) sda = (sda_en_o) ? sda_o : 1'b1;

// Signals to connect the sink
logic sim_sram_clk;
logic sim_sram_rst;
Expand Down Expand Up @@ -240,6 +263,7 @@ module tb;
uvm_config_db#(virtual clk_rst_if)::set(null, "*", "sys_clk_if", sys_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_pins_if);
uvm_config_db#(virtual i2c_if)::set(null, "*.env.m_i2c_agent", "vif", i2c_if);

// SW logger and test status interfaces.
uvm_config_db#(virtual sw_test_status_if)::set(
Expand Down
9 changes: 8 additions & 1 deletion hw/top_chip/dv/top_chip_sim_cfg.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,14 @@
sw_images: ["i2c_host_tx_rx_test_vanilla_sram:5" "bootrom:5"]
run_opts: ["+ChipMemSRAM_image_file={run_dir}/i2c_host_tx_rx_test_vanilla_sram.vmem",
"+ChipMemROM_image_file={run_dir}/bootrom.vmem"]
}
}
{
name: i2c_host_tx_rx_cheri
uvm_test_seq: top_chip_dv_i2c_host_tx_rx_vseq
sw_images: ["i2c_host_tx_rx_test_cheri_sram:5" "bootrom:5"]
run_opts: ["+ChipMemSRAM_image_file={run_dir}/i2c_host_tx_rx_test_cheri_sram.vmem",
"+ChipMemROM_image_file={run_dir}/bootrom.vmem"]
}
{
name: i2c_device_tx_rx
uvm_test_seq: top_chip_dv_i2c_device_tx_rx_vseq
Expand Down
9 changes: 6 additions & 3 deletions sw/device/examples/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ int main(void)
while (true) {
timer_busy_sleep_us(timer, 1000u);

uint8_t w_data = 0;
// Read current temperature from an AS6212 I^2C-bus sensor and print the value
if (i2c_write_byte(i2c, 0x48u, 0u)) { // select TVAL reg; also a presence check
uint16_t sensor_reading = i2c_read_byte(i2c, 0x48u); // read TVAl reg
if (sensor_reading != 0xFF) { // only print if we get a non-error value
i2c_write_n_bytes(i2c, 0x48u, &w_data, 1);
if (check_wr_xfer_status(i2c)) { // select TVAL reg; also a presence check
i2c_read_n_bytes(i2c, 0x48u, 1);
if (check_rd_xfer_status(i2c)) {
uint16_t sensor_reading = i2c_rdata_byte(i2c); // read TVAl reg
uprintf(uart, "Temperature: 0x%x degC\n",
(sensor_reading << 1)); // no decimal printf
}
Expand Down
83 changes: 57 additions & 26 deletions sw/device/lib/hal/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ void i2c_init(i2c_t i2c)
VOLATILE_WRITE(i2c->timing4, t4_reg);
}

bool i2c_write_byte(i2c_t i2c, uint8_t addr, uint8_t data)
void i2c_write_n_bytes(i2c_t i2c, uint8_t addr, const uint8_t *data, uint8_t num_wr_bytes)
{
// Reset FMT FIFO (because we currently don't clean-up after errors)
// Reset the FMT fifo as a precautionary step in case something goes wrong when controller's FSM
// was halted and the SW didn't manage to clear the fifo during that scenario.
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

Expand All @@ -74,31 +75,22 @@ bool i2c_write_byte(i2c_t i2c, uint8_t addr, uint8_t data)
fdata_reg.start = 1u;
VOLATILE_WRITE(i2c->fdata, fdata_reg);

// Send stop bit and data
fdata_reg.fbyte = data;
fdata_reg.start = 0;
fdata_reg.stop = 1u;
VOLATILE_WRITE(i2c->fdata, fdata_reg);

// Wait for transaction to complete and report simple succeed/fail
for (uint32_t ii = 0; ii < 10000000ul /*arbitrary number*/; ii++) {
i2c_intr i2c_intr_state_reg = VOLATILE_READ(i2c->intr_state);
if (i2c_intr_state_reg & i2c_intr_controller_halt) {
return false; // transaction failed
}
if (i2c_intr_state_reg & i2c_intr_cmd_complete) {
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);
if (i2c_status_reg & i2c_status_fmtempty) {
return true; // transaction succeeded
}
// Send all data bytes; assert STOP only on the last byte
for (uint8_t i = 0; i < num_wr_bytes; i++) {
fdata_reg.fbyte = data[i];
if (i == (num_wr_bytes - 1u)) {
fdata_reg.stop = 1u;
}
VOLATILE_WRITE(i2c->fdata, fdata_reg);
}
return false; // timeout
}

uint8_t i2c_read_byte(i2c_t i2c, uint8_t addr)
void i2c_read_n_bytes(i2c_t i2c, uint8_t addr, uint8_t num_rd_bytes)
{
// Reset FMT FIFO (because we currently don't clean-up after errors)
// Reset the FMT fifo as a precautionary step in case something goes wrong when controller's FSM
// was halted and the SW didn't manage to clear the fifo during that scenario.
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

Expand All @@ -112,28 +104,67 @@ uint8_t i2c_read_byte(i2c_t i2c, uint8_t addr)

// Send stop bit, read bit and number of bytes to read
fdata_reg.readb = 1u;
fdata_reg.fbyte = 1u; // If readb = 1 then fbyte contains the number of bytes to read
fdata_reg.fbyte = num_rd_bytes; // If readb = 1 then fbyte contains the number of bytes to read
fdata_reg.start = 0;
fdata_reg.stop = 1u;
VOLATILE_WRITE(i2c->fdata, fdata_reg);
}

bool check_wr_xfer_status(i2c_t i2c)
{
// Wait for transaction to complete and report simple succeed / fail
for (uint32_t ii = 0; ii < 10000000ul /*arbitrary number*/; ii++) {
i2c_intr i2c_intr_state_reg = VOLATILE_READ(i2c->intr_state);
if (i2c_intr_state_reg & i2c_intr_controller_halt) {
// Reset FMT FIFO as controller's FSM is in halt
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

// The other thing to do at this point is to clear the CONTROLLER_EVENTS register in
// order to clear the controller halt interrupt. Since, we want to fail if a halt event
// occured in CONTROLLER_EVENTS register there is no such point to clear that register.
return false; // Transaction failed
}
if (i2c_intr_state_reg & i2c_intr_cmd_complete) {
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);
if (i2c_status_reg & i2c_status_fmtempty) {
return true; // transaction succeeded
}
}
}
return false; // Timeout
}

bool check_rd_xfer_status(i2c_t i2c)
{
// Wait for transaction to complete and return either read data or 0xFF
for (uint32_t ii = 0; ii < 10000000ul /*arbitrary number*/; ii++) {
i2c_intr i2c_intr_state_reg = VOLATILE_READ(i2c->intr_state);
if (i2c_intr_state_reg & i2c_intr_controller_halt) {
return 0xFF; // transaction failed
// Reset FMT FIFO as controller's FSM is in halt
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

// The other thing to do at this point is to clear the CONTROLLER_EVENTS register in
// order to clear the controller halt interrupt. Since, we want to fail if a halt event
// occured in CONTROLLER_EVENTS register there is no such point to clear that register.
return false; // Transaction failed
}
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);
if (i2c_status_reg & i2c_status_fmtempty) {
// transaction succeeded, return read data
i2c_rdata rdata_reg = VOLATILE_READ(i2c->rdata);
return rdata_reg.rdata;
return true;
}
}
return 0xFF; // timeout
return false; // Timeout
}

void enable_controller_mode(i2c_t i2c)
{
VOLATILE_WRITE(i2c->ctrl, i2c_ctrl_enablehost);
}

uint8_t i2c_rdata_byte(i2c_t i2c)
{
i2c_rdata rdata_reg = VOLATILE_READ(i2c->rdata);
return rdata_reg.rdata;
}
16 changes: 14 additions & 2 deletions sw/device/lib/hal/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,20 @@

void i2c_init(i2c_t i2c);

bool i2c_write_byte(i2c_t i2c, uint8_t addr, uint8_t data);
uint8_t i2c_read_byte(i2c_t i2c, uint8_t addr);
// Transmits multiple bytes to the target
void i2c_write_n_bytes(i2c_t i2c, uint8_t addr, const uint8_t *data, uint8_t num_wr_bytes);

// Receive a n bytes from the target
void i2c_read_n_bytes(i2c_t i2c, uint8_t addr, uint8_t num_rd_bytes);

// Check if the write was successful
bool check_rd_xfer_status(i2c_t i2c);

// checks if the read was successful
bool check_wr_xfer_status(i2c_t i2c);

// Enable I2C in controller mode
void enable_controller_mode(i2c_t i2c);

// Return the data in the target's tx fifo
uint8_t i2c_rdata_byte(i2c_t i2c);
1 change: 1 addition & 0 deletions sw/device/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mocha_add_test(NAME gpio_reg_access_test SOURCES gpio/reg_access_test.c LIBRARIE
# driven externally
mocha_add_test(NAME gpio_smoketest SOURCES gpio/smoketest.c LIBRARIES ${LIBS} SKIP_VERILATOR SKIP_FPGA)
mocha_add_test(NAME i2c_smoketest SOURCES i2c/smoketest.c LIBRARIES ${LIBS})
mocha_add_test(NAME i2c_host_tx_rx_test SOURCES i2c/host_test.c LIBRARIES ${LIBS})
mocha_add_test(NAME mailbox_smoketest SOURCES mailbox/smoketest.c LIBRARIES ${LIBS})
mocha_add_test(NAME plic_smoketest SOURCES plic/smoketest.c LIBRARIES ${LIBS})
mocha_add_test(NAME rstmgr_software_reset SOURCES rstmgr/software_reset.c LIBRARIES ${LIBS})
Expand Down
Loading
Loading