[dv, sw] UART logs to UVM prints
This PR enables logs from SW test sent over UART to be captured as UVM
print messages. The printed message looks for severity identifier in the
message string (INFO, WARN, ERROR and FATAL) and prints the final
message with the corresponding uvm_* facilities.
The plan to enable the much faster print-bypassing-uart is still on -
that will be enabled shortly. The goal is to have both methods supported
so that FPGA tests can be run as-is in DV without any modifications.
Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/hw/dv/data/sim.mk b/hw/dv/data/sim.mk
index 8c3b70b..fa70dce 100644
--- a/hw/dv/data/sim.mk
+++ b/hw/dv/data/sim.mk
@@ -52,7 +52,7 @@
ifneq (${sw_name},)
# Initialize meson build system.
cd ${proj_root} && \
- BUILD_ROOT=${sw_build_dir} ${proj_root}/meson_init.sh
+ BUILD_ROOT=${sw_build_dir} ${proj_root}/meson_init.sh -f
# Compile boot rom code and generate the image.
ninja -C ${sw_build_dir}/build-out \
sw/device/boot_rom/boot_rom_export_${sw_build_device}
diff --git a/hw/dv/sv/uart_agent/uart_agent.core b/hw/dv/sv/uart_agent/uart_agent.core
index 56a10c3..51dd3ba 100644
--- a/hw/dv/sv/uart_agent/uart_agent.core
+++ b/hw/dv/sv/uart_agent/uart_agent.core
@@ -18,6 +18,7 @@
- uart_driver.sv: {is_include_file: true}
- uart_monitor.sv: {is_include_file: true}
- uart_sequencer.sv: {is_include_file: true}
+ - uart_logger.sv: {is_include_file: true}
- uart_agent.sv: {is_include_file: true}
- seq_lib/uart_seq_list.sv: {is_include_file: true}
- seq_lib/uart_base_seq.sv: {is_include_file: true}
diff --git a/hw/dv/sv/uart_agent/uart_agent.sv b/hw/dv/sv/uart_agent/uart_agent.sv
index fc1eb91..9f2d4fd 100644
--- a/hw/dv/sv/uart_agent/uart_agent.sv
+++ b/hw/dv/sv/uart_agent/uart_agent.sv
@@ -11,6 +11,8 @@
);
`uvm_component_utils(uart_agent)
+ uart_logger m_logger;
+
`uvm_component_new
function void build_phase(uvm_phase phase);
@@ -19,6 +21,24 @@
if (!uvm_config_db#(virtual uart_if)::get(this, "", "vif", cfg.vif)) begin
`uvm_fatal(`gfn, "failed to get uart_if handle from uvm_config_db")
end
+
+ // Create the logger instance.
+ if (cfg.en_logger) begin
+ m_logger = uart_logger::type_id::create("m_logger", this);
+ m_logger.cfg = cfg;
+ end
+ endfunction
+
+ function void connect_phase(uvm_phase phase);
+ super.connect_phase(phase);
+ // Connect the logger TLM port to the monitor.
+ if (cfg.en_logger) begin
+ if (cfg.use_rx_for_logger) begin
+ monitor.rx_analysis_port.connect(m_logger.log_item_fifo.analysis_export);
+ end else begin
+ monitor.tx_analysis_port.connect(m_logger.log_item_fifo.analysis_export);
+ end
+ end
endfunction
endclass
diff --git a/hw/dv/sv/uart_agent/uart_agent_cfg.sv b/hw/dv/sv/uart_agent/uart_agent_cfg.sv
index 3b11121..97b0ee5 100644
--- a/hw/dv/sv/uart_agent/uart_agent_cfg.sv
+++ b/hw/dv/sv/uart_agent/uart_agent_cfg.sv
@@ -16,6 +16,11 @@
bit en_parity;
bit odd_parity;
+ // Logger settings.
+ bit en_logger = 1'b0; // enable logger on tx
+ bit use_rx_for_logger = 1'b0; // use rx instead of tx
+ string logger_msg_id = "UART_LOGGER";
+
// reset is controlled at upper seq-level as no reset pin on uart interface
bit under_reset;
@@ -23,10 +28,15 @@
virtual uart_if vif;
`uvm_object_utils_begin(uart_agent_cfg)
- `uvm_field_int(is_active, UVM_DEFAULT)
- `uvm_field_int(en_cov, UVM_DEFAULT)
- `uvm_field_int(en_rx_checks, UVM_DEFAULT)
- `uvm_field_int(en_tx_checks, UVM_DEFAULT)
+ `uvm_field_int(is_active, UVM_DEFAULT)
+ `uvm_field_int(en_cov, UVM_DEFAULT)
+ `uvm_field_int(en_rx_checks, UVM_DEFAULT)
+ `uvm_field_int(en_tx_checks, UVM_DEFAULT)
+ `uvm_field_int(en_tx_monitor, UVM_DEFAULT)
+ `uvm_field_int(en_rx_monitor, UVM_DEFAULT)
+ `uvm_field_int(en_parity, UVM_DEFAULT)
+ `uvm_field_int(odd_parity, UVM_DEFAULT)
+ `uvm_field_enum(baud_rate_e, baud_rate, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
@@ -55,4 +65,5 @@
en_tx_monitor = 1;
end
endfunction
+
endclass
diff --git a/hw/dv/sv/uart_agent/uart_agent_pkg.sv b/hw/dv/sv/uart_agent/uart_agent_pkg.sv
index 54eaa70..12ddb48 100644
--- a/hw/dv/sv/uart_agent/uart_agent_pkg.sv
+++ b/hw/dv/sv/uart_agent/uart_agent_pkg.sv
@@ -55,6 +55,7 @@
`include "uart_monitor.sv"
`include "uart_driver.sv"
`include "uart_sequencer.sv"
+ `include "uart_logger.sv"
`include "uart_agent.sv"
`include "uart_seq_list.sv"
diff --git a/hw/dv/sv/uart_agent/uart_logger.sv b/hw/dv/sv/uart_agent/uart_logger.sv
new file mode 100644
index 0000000..3e2c2f2
--- /dev/null
+++ b/hw/dv/sv/uart_agent/uart_logger.sv
@@ -0,0 +1,80 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class uart_logger extends uvm_component;
+ `uvm_component_utils(uart_logger)
+
+ uart_agent_cfg cfg;
+ uvm_tlm_analysis_fifo #(uart_item) log_item_fifo;
+
+ `uvm_component_new
+
+ virtual function void build_phase(uvm_phase phase);
+ log_item_fifo = new("log_item_fifo", this);
+ endfunction
+
+ virtual task run_phase(uvm_phase phase);
+ capture_logs();
+ endtask
+
+ // Captures bytes received from UART TX port and constructs the logs for printing.
+ virtual task capture_logs();
+ uart_item item;
+ string char;
+ string log;
+ byte lf = 8'ha;
+ byte cr = 8'hd;
+
+ fork
+ forever begin
+ log_item_fifo.get(item);
+ char = string'(item.data);
+ `uvm_info(cfg.logger_msg_id, $sformatf("received char: %0s", char), UVM_DEBUG)
+ // Continue concatenating chars into the log string untl lf or cr is encountered.
+ if (item.data inside {lf, cr}) begin
+ print_log(log);
+ log = "";
+ end
+ else begin
+ log = {log, char};
+ end
+ end
+ forever begin
+ // reset thread - if reset occurs, reset the log to an empty string.
+ @(cfg.under_reset);
+ if (cfg.under_reset) log = "";
+ end
+ join
+ endtask
+
+ // Print log with the right severity. Severity is extracted from the log string.
+ // See sw/device/lib/base/log.h for details on how the severity is indicated.
+ // TODO: Add support for verbosity when the severity is info.
+ virtual function void print_log(string log);
+ string info = "INFO: *";
+ string warn = "WARNING: *";
+ string error = "ERROR: *";
+ string fatal = "FATAL: *";
+
+ if (log == "") return;
+ case (1)
+ (!uvm_re_match(info, log)): begin
+ `uvm_info(cfg.logger_msg_id, log.substr(info.len() - 1, log.len() - 1), UVM_LOW)
+ end
+ (!uvm_re_match(warn, log)): begin
+ `uvm_warning(cfg.logger_msg_id, log.substr(warn.len() - 1, log.len() - 1))
+ end
+ (!uvm_re_match(error, log)): begin
+ `uvm_error(cfg.logger_msg_id, log.substr(error.len() - 1, log.len() - 1))
+ end
+ (!uvm_re_match(fatal, log)): begin
+ `uvm_fatal(cfg.logger_msg_id, log.substr(fatal.len() - 1, log.len() - 1))
+ end
+ default: begin
+ `uvm_info(cfg.logger_msg_id, log, UVM_LOW)
+ end
+ endcase
+ endfunction
+
+endclass
diff --git a/hw/dv/sv/uart_agent/uart_monitor.sv b/hw/dv/sv/uart_agent/uart_monitor.sv
index e43f6c0..5ca3354 100644
--- a/hw/dv/sv/uart_agent/uart_monitor.sv
+++ b/hw/dv/sv/uart_agent/uart_monitor.sv
@@ -192,4 +192,5 @@
obj_raised[dir] = 1'b0;
end
endfunction
+
endclass
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 4936cca..669870c 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -40,7 +40,7 @@
// Default UVM test and seq class name.
uvm_test: chip_base_test
uvm_test_seq: chip_base_vseq
- sw_build_device: fpga_nexysvideo
+ sw_build_device: sim_dv
// Additional option to RAL generation for top level.
gen_ral_pkg_opts: ["--top"]
diff --git a/hw/top_earlgrey/dv/env/chip_env_cfg.sv b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
index a6808fd..bd17c65 100644
--- a/hw/top_earlgrey/dv/env/chip_env_cfg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
@@ -4,7 +4,10 @@
class chip_env_cfg extends dv_base_env_cfg #(.RAL_T(chip_reg_block));
+ // Testbench settings
bit stub_cpu;
+ bit en_uart_logger = 1'b1;
+ bit use_gpio_for_sw_test_status;
// chip top interfaces
virtual clk_rst_if usb_clk_rst_vif;
@@ -37,6 +40,12 @@
`uvm_field_object(m_cpu_d_tl_agent_cfg, UVM_DEFAULT)
`uvm_object_utils_end
+ // TODO: Fixing core clk freq to 50MHz for now.
+ // Need to find a way to pass this to the SW test.
+ constraint clk_freq_mhz_c {
+ clk_freq_mhz == dv_utils_pkg::ClkFreq50Mhz;
+ }
+
`uvm_object_new
// TODO review value for csr_base_addr, csr_addr_map_size
@@ -51,8 +60,6 @@
super.initialize(csr_base_addr);
// create uart agent config obj
m_uart_agent_cfg = uart_agent_cfg::type_id::create("m_uart_agent_cfg");
- m_uart_agent_cfg.en_tx_monitor = 1'b0;
- m_uart_agent_cfg.en_rx_monitor = 1'b0;
// create jtag agent config obj
m_jtag_agent_cfg = jtag_agent_cfg::type_id::create("m_jtag_agent_cfg");
// create spi agent config obj
diff --git a/hw/top_earlgrey/dv/env/chip_env_pkg.sv b/hw/top_earlgrey/dv/env/chip_env_pkg.sv
index edd106a..008c41c 100644
--- a/hw/top_earlgrey/dv/env/chip_env_pkg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_pkg.sv
@@ -64,4 +64,5 @@
`include "chip_scoreboard.sv"
`include "chip_env.sv"
`include "chip_vseq_list.sv"
+
endpackage
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
index a0bdc97..ad4eb11 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
@@ -58,7 +58,10 @@
virtual task dut_init(string reset_kind = "HARD");
// Set default frequencies.
cfg.usb_clk_rst_vif.set_freq_mhz(dv_utils_pkg::ClkFreq48Mhz);
- cfg.m_uart_agent_cfg.set_baud_rate(BaudRate1Mbps);
+ // Set 'default' UART baud rate of 2Mbps - this is also programmed by the C test.
+ // TODO: Fixing this for now - need to find a way to pass this on to the SW test.
+ cfg.m_uart_agent_cfg.set_parity(1'b0, 1'b0);
+ cfg.m_uart_agent_cfg.set_baud_rate(BaudRate2Mbps);
// Initialize gpio pin default states
cfg.gpio_vif.set_pulldown_en({chip_env_pkg::NUM_GPIOS{1'b1}});
// Bring the chip out of reset.
diff --git a/hw/top_earlgrey/dv/tests/chip_base_test.sv b/hw/top_earlgrey/dv/tests/chip_base_test.sv
index 4bb2b01..19e5a5f 100644
--- a/hw/top_earlgrey/dv/tests/chip_base_test.sv
+++ b/hw/top_earlgrey/dv/tests/chip_base_test.sv
@@ -22,7 +22,11 @@
void'($value$plusargs("stub_cpu=%0b", cfg.stub_cpu));
// Set tl_agent's is_active bit based on the retrieved stub_cpu value.
cfg.m_cpu_d_tl_agent_cfg.is_active = cfg.stub_cpu;
- endfunction : build_phase
+ // knob to enable logging via uart
+ void'($value$plusargs("en_uart_logger=%0b", cfg.en_uart_logger));
+ cfg.m_uart_agent_cfg.en_logger = cfg.en_uart_logger;
+ cfg.m_uart_agent_cfg.logger_msg_id = "SW_LOGS";
+ endfunction : build_phase
endclass : chip_base_test
diff --git a/sw/device/lib/arch/device.h b/sw/device/lib/arch/device.h
index 8941eff..d15d33f 100644
--- a/sw/device/lib/arch/device.h
+++ b/sw/device/lib/arch/device.h
@@ -24,6 +24,10 @@
*/
typedef enum device_type {
/**
+ * Represents "DV", i.e. running th test in a DV simulation testbench.
+ */
+ kDeviceSimDV,
+ /**
* Represents the "Verilator" device, i.e., a synthesis of the OpenTitan
* design by Verilator into C++.
*/
diff --git a/sw/device/lib/arch/device_sim_dv.c b/sw/device/lib/arch/device_sim_dv.c
new file mode 100644
index 0000000..6c91bf6
--- /dev/null
+++ b/sw/device/lib/arch/device_sim_dv.c
@@ -0,0 +1,18 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/arch/device.h"
+
+/**
+ * Device-specific symbol definitions for the Verilator device.
+ */
+
+const device_type_t kDeviceType = kDeviceSimDV;
+
+// TODO: DV testbench completely randomizes these. Need to add code to
+// retrieve these from a preloaded memory location set by the testbench.
+
+const uint64_t kClockFreqHz = 50 * 1000 * 1000; // 50MHz
+
+const uint64_t kUartBaudrate = 2 * (1 << 20); // 2Mib/s
diff --git a/sw/device/lib/arch/meson.build b/sw/device/lib/arch/meson.build
index 10654cf..9f854db 100644
--- a/sw/device/lib/arch/meson.build
+++ b/sw/device/lib/arch/meson.build
@@ -2,6 +2,13 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
+sw_lib_arch_sim_dv = declare_dependency(
+ link_with: static_library(
+ 'device_sim_dv',
+ sources: ['device_sim_dv.c'],
+ ),
+)
+
sw_lib_arch_sim_verilator = declare_dependency(
link_with: static_library(
'device_sim_verilator',
@@ -21,6 +28,7 @@
# Effectively, this is all devices that are not specialized DV
# testbenches.
sw_lib_arch_core_devices = {
+ 'sim_dv': sw_lib_arch_sim_dv,
'sim_verilator': sw_lib_arch_sim_verilator,
'fpga_nexysvideo': sw_lib_arch_fpga_nexysvideo,
-}
\ No newline at end of file
+}