[uart/dv] Update scb for TL-UL interface change
1. add tx_processing_item_q to store processing item
2. update scb for txlvl, txempty
diff --git a/hw/dv/sv/uart_agent/uart_agent_cov.sv b/hw/dv/sv/uart_agent/uart_agent_cov.sv
index 7f0c768..cd2dd30 100644
--- a/hw/dv/sv/uart_agent/uart_agent_cov.sv
+++ b/hw/dv/sv/uart_agent/uart_agent_cov.sv
@@ -11,9 +11,7 @@
cp_data: coverpoint item.data;
cp_en_parity: coverpoint cfg.en_parity;
cp_odd_parity: coverpoint cfg.odd_parity;
- cp_baud_rate: coverpoint cfg.baud_rate {
- illegal_bins illegal = {BaudRate0};
- }
+ cp_baud_rate: coverpoint cfg.baud_rate;
cross cp_dir, cp_data, cp_en_parity, cp_odd_parity, cp_baud_rate;
endgroup
diff --git a/hw/dv/sv/uart_agent/uart_agent_pkg.sv b/hw/dv/sv/uart_agent/uart_agent_pkg.sv
index f75999c..405930a 100644
--- a/hw/dv/sv/uart_agent/uart_agent_pkg.sv
+++ b/hw/dv/sv/uart_agent/uart_agent_pkg.sv
@@ -18,7 +18,6 @@
} uart_dir_e;
typedef enum int {
- BaudRate0 = 0, // none
BaudRate9600 = 9600,
BaudRate115200 = 115200,
BaudRate230400 = 230400,
@@ -37,13 +36,11 @@
function automatic real get_baud_rate_period_ns(baud_rate_e baud_rate);
// return 10^9 / baud_rate ns upto 3 decimal places
case(baud_rate)
- BaudRate0 : return 0.0;
BaudRate9600 : return 104166.667;
BaudRate115200: return 8680.556;
BaudRate230400: return 4340.278;
BaudRate1Mbps : return 953.674;
BaudRate2Mbps : return 476.837;
- default : return 0.0;
endcase
endfunction
diff --git a/hw/ip/uart/dv/Makefile b/hw/ip/uart/dv/Makefile
index 081c108..e6fb737 100644
--- a/hw/ip/uart/dv/Makefile
+++ b/hw/ip/uart/dv/Makefile
@@ -33,7 +33,7 @@
UVM_TEST_SEQ = uart_sanity_vseq
endif
-ifeq (${TEST_NAME},uart_random)
+ifeq (${TEST_NAME},uart_tx_rx)
UVM_TEST_SEQ = uart_tx_rx_vseq
endif
diff --git a/hw/ip/uart/dv/env/seq_lib/uart_base_vseq.sv b/hw/ip/uart/dv/env/seq_lib/uart_base_vseq.sv
index 9fc9e4c..4dc6d28 100644
--- a/hw/ip/uart/dv/env/seq_lib/uart_base_vseq.sv
+++ b/hw/ip/uart/dv/env/seq_lib/uart_base_vseq.sv
@@ -26,7 +26,6 @@
bit do_interrupt = 1'b1;
constraint baud_rate_c {
- baud_rate != BaudRate0;
// constrain nco not over nco.get_n_bits
`CALC_NCO(baud_rate, p_sequencer.cfg.clk_freq_mhz) <
2 ** p_sequencer.cfg.ral.ctrl.nco.get_n_bits();
diff --git a/hw/ip/uart/dv/env/seq_lib/uart_fifo_overflow_vseq.sv b/hw/ip/uart/dv/env/seq_lib/uart_fifo_overflow_vseq.sv
index 4eaa5e2..4330e86 100644
--- a/hw/ip/uart/dv/env/seq_lib/uart_fifo_overflow_vseq.sv
+++ b/hw/ip/uart/dv/env/seq_lib/uart_fifo_overflow_vseq.sv
@@ -15,18 +15,4 @@
virtual task wait_for_rx_fifo_not_full();
endtask : wait_for_rx_fifo_not_full
- // special handle for read rx when testing overflow
- virtual task rand_read_rx_byte(uint weight_to_skip);
- bit [TL_DW-1:0] status;
-
- csr_rd(.ptr(ral.status), .value(status));
- if (get_field_val(ral.status.rxfull, status)) begin
- // the rx item is abort to be completedly collected, don't know if drop it or not when rx fifo
- // is read at this period. Do not read at this cycle
- wait_when_in_ignored_period(.rx(1));
- end
-
- super.rand_read_rx_byte(weight_to_skip);
- endtask : rand_read_rx_byte
-
endclass : uart_fifo_overflow_vseq
diff --git a/hw/ip/uart/dv/env/seq_lib/uart_intr_vseq.sv b/hw/ip/uart/dv/env/seq_lib/uart_intr_vseq.sv
index fbd1ebe..504159c 100644
--- a/hw/ip/uart/dv/env/seq_lib/uart_intr_vseq.sv
+++ b/hw/ip/uart/dv/env/seq_lib/uart_intr_vseq.sv
@@ -111,6 +111,12 @@
int level = ral.ctrl.rxblvl.get_mirrored_value();
int break_bytes = get_break_bytes_by_level(level);
+ // drive one good rx char to reset DUT break cnt (allzero_cnt)
+ drive_rx_bytes(.num_bytes(1));
+ // clear rx fifo and interrupts triggered by above driving
+ clear_fifos(.clear_tx_fifo(0), .clear_rx_fifo(1));
+ csr_wr(.csr(ral.intr_state), .value('hff));
+
fork
begin
drive_rx_all_0s();
@@ -284,6 +290,8 @@
`DV_CHECK_STD_RANDOMIZE_FATAL(tx_byte)
send_tx_byte(.data(tx_byte));
end
+ // wait for 1 cycle to allow interrupt triggered
+ cfg.clk_rst_vif.wait_clks(1);
endtask : drive_tx_bytes
task drive_rx_bytes(int num_bytes);
diff --git a/hw/ip/uart/dv/env/seq_lib/uart_sanity_vseq.sv b/hw/ip/uart/dv/env/seq_lib/uart_sanity_vseq.sv
index 4867b3f..385dce8 100644
--- a/hw/ip/uart/dv/env/seq_lib/uart_sanity_vseq.sv
+++ b/hw/ip/uart/dv/env/seq_lib/uart_sanity_vseq.sv
@@ -41,6 +41,8 @@
`DV_CHECK_STD_RANDOMIZE_FATAL(tx_byte)
send_tx_byte(tx_byte);
+ // if no delay in TL-UL trans, DUT takes 1 more cycle to update status reg
+ cfg.clk_rst_vif.wait_clks(1);
spinwait_txidle();
end
endtask : process_tx
diff --git a/hw/ip/uart/dv/env/uart_scoreboard.sv b/hw/ip/uart/dv/env/uart_scoreboard.sv
index 9f3e1cc..2917521 100644
--- a/hw/ip/uart/dv/env/uart_scoreboard.sv
+++ b/hw/ip/uart/dv/env/uart_scoreboard.sv
@@ -21,7 +21,8 @@
local bit [NumUartIntr-1:0] intr_exp;
local bit [7:0] rdata_exp;
// store tx/rx_q at TL address phase
- local int tx_q_size_at_addr_ph, rx_q_size_at_addr_ph;
+ local int tx_q_size_at_addr_phase, rx_q_size_at_addr_phase;
+ local bit [NumUartIntr-1:0] intr_exp_at_addr_phase;
// TLM fifos to pick up the packets
uvm_tlm_analysis_fifo #(uart_item) uart_tx_fifo;
@@ -29,6 +30,7 @@
// local queues to hold incoming packets pending comparison
uart_item tx_q[$];
+ uart_item tx_processing_item_q[$];
uart_item rx_q[$];
bit obj_raised = 1'b0;
@@ -60,12 +62,14 @@
uart_tx_fifo.get(act_item);
`uvm_info(`gfn, $sformatf("received uart tx item:\n%0s", act_item.sprint()), UVM_HIGH)
- `DV_CHECK_GT(tx_q.size(), 0)
- exp_item = tx_q.pop_front();
+ `DV_CHECK_EQ(tx_processing_item_q.size(), 1)
+ exp_item = tx_processing_item_q.pop_front();
+ // move item from fifo to process
+ if (tx_q.size > 0) tx_processing_item_q.push_back(tx_q.pop_front());
`uvm_info(`gfn, $sformatf("After drop one item, new tx_q size: %0d", tx_q.size), UVM_HIGH)
compare(act_item, exp_item, "TX");
- if (tx_q.size() == 0) process_objections(1'b0);
+ if (tx_q.size() == 0 && tx_processing_item_q.size() == 0) process_objections(1'b0);
end
endtask
@@ -104,28 +108,14 @@
end // forever
endtask
- virtual function void predict_tx_watermark_intr(bit adding_new_item = 0);
+ virtual function void predict_tx_watermark_intr(uint tx_q_size = tx_q.size);
uint watermark = get_watermark_bytes_by_level(ral.fifo_ctrl.txilvl.get_mirrored_value());
- uint tx_q_size = tx_q.size;
- uint act_fifo_size;
-
- if (tx_q_size == 0) return;
-
- // when tx is enabled, tx_q_size is act_fifo_size + 1 (a processing item)
- // when tx is enabled and received 1st item, act_fifo_size becomes 1 and drop to 0 in one
- // cycle, use act_fifo_size=1 to calculate the watermark interrupt
- if (tx_enabled) begin
- if (tx_q_size == 1 && adding_new_item) act_fifo_size = tx_q_size;
- else act_fifo_size = tx_q_size - 1;
- end else begin
- act_fifo_size = tx_q_size;
- end
- intr_exp[TxWatermark] |= act_fifo_size >= watermark;
+ intr_exp[TxWatermark] |= (tx_q_size >= watermark);
endfunction
- virtual function void predict_rx_watermark_intr();
+ virtual function void predict_rx_watermark_intr(uint rx_q_size = rx_q.size);
uint watermark = get_watermark_bytes_by_level(ral.fifo_ctrl.rxilvl.get_mirrored_value());
- intr_exp[RxWatermark] |= (rx_q.size >= watermark);
+ intr_exp[RxWatermark] |= (rx_q_size >= watermark);
endfunction
// we don't model uart cycle-acurrately, ignore checking when item is just/almost finished
@@ -160,8 +150,8 @@
uart_tx_clk_pulses = uart_vif.uart_tx_clk_pulses;
uart_rx_clk_pulses = uart_vif.uart_rx_clk_pulses;
// save fifo level at address phase
- tx_q_size_at_addr_ph = tx_q.size;
- rx_q_size_at_addr_ph = rx_q.size;
+ tx_q_size_at_addr_phase = tx_q.size;
+ rx_q_size_at_addr_phase = rx_q.size;
end
// process the csr req
@@ -173,7 +163,10 @@
tx_enabled = ral.ctrl.tx.get_mirrored_value();
rx_enabled = ral.ctrl.rx.get_mirrored_value();
// if tx_q is not empty and tx got enabled, raise objection since we have work to do
- if (tx_q.size() > 0 && tx_enabled) begin
+ if ((tx_q.size > 0 || tx_processing_item_q.size > 0) && tx_enabled) begin
+ if (tx_q.size > 0 && tx_processing_item_q.size == 0) begin
+ tx_processing_item_q.push_back(tx_q.pop_front());
+ end
process_objections(1'b1);
end
end
@@ -181,46 +174,48 @@
"wdata": begin
// if tx is enabled, push exp tx data to q
if (write && channel == AddrChannel) begin
- uint max_tx_size = UART_FIFO_DEPTH;
- uart_item tx_item = uart_item::type_id::create("tx_item");;
- // if tx is enabled, fifo size is UART_FIFO_DEPTH and one item is sending
- // totally is UART_FIFO_DEPTH + 1
- if (tx_enabled) max_tx_size += 1;
+ uart_item tx_item = uart_item::type_id::create("tx_item");;
tx_item.data = csr.get_mirrored_value();
- if (tx_q.size() < max_tx_size) begin
+ if (tx_q.size() < UART_FIFO_DEPTH) begin
tx_q.push_back(tx_item);
- predict_tx_watermark_intr(.adding_new_item(1));
`uvm_info(`gfn, $sformatf("After push one item, tx_q size: %0d", tx_q.size), UVM_HIGH)
+ end else begin
+ intr_exp[TxOverflow] = 1;
+ `uvm_info(`gfn, $sformatf(
+ "Drop tx item: %0h, tx_q size: %0d, uart_tx_clk_pulses: %0d",
+ csr.get_mirrored_value(), tx_q.size(), uart_tx_clk_pulses), UVM_MEDIUM)
+ end
+ // it takes 3 cycles to move item from fifo to process, which delays reg status change
+ // and it also takes 3 cycles to trigger tx matermark interrupt
+ fork begin
+ uint latch_tx_q_size = tx_q.size();
+ cfg.clk_rst_vif.wait_n_clks(3); // use negedge to avoid race condition
+ predict_tx_watermark_intr(latch_tx_q_size);
if (ral.ctrl.slpbk.get_mirrored_value()) begin
// if sys loopback is on, tx item isn't sent to uart pin but rx fifo
- void'(tx_q.pop_front());
+ uart_item tx_item = tx_q.pop_front();
if (rx_enabled && (rx_q.size < UART_FIFO_DEPTH)) begin
rx_q.push_back(tx_item);
predict_rx_watermark_intr();
end
- end else if (tx_enabled) begin
+ end else if (tx_enabled && tx_processing_item_q.size == 0 && tx_q.size > 0) begin
+ tx_processing_item_q.push_back(tx_q.pop_front());
// raise objection if tx is enabled - there is work to do
process_objections(1'b1);
end
- end else begin
- intr_exp[TxOverflow] = 1;
- `uvm_info(`gfn, $sformatf(
- "Drop tx item: %0h, tx_q size: %0d, max_tx_size: %0d, uart_tx_clk_pulses: %0d",
- csr.get_mirrored_value(), tx_q.size(), max_tx_size, uart_tx_clk_pulses), UVM_MEDIUM)
- end
+ end join_none
end // write && channel == AddrChannel
end
"fifo_ctrl": begin
if (write && channel == AddrChannel) begin
// these fields pulse & read back 0
if (ral.fifo_ctrl.txrst.get_mirrored_value()) begin
- if (tx_enabled && tx_q.size > 0) begin
+ if (tx_enabled && tx_q.size > 0 && tx_processing_item_q.size == 0) begin
// keep the 1st item in the queue, as it's being sent
- tx_q = {tx_q[0]};
- end else begin
- tx_q.delete();
+ tx_processing_item_q.push_back(tx_q.pop_front());
end
+ tx_q.delete();
ral.fifo_ctrl.txrst.predict(.value(0), .kind(UVM_PREDICT_WRITE));
if (!tx_enabled) process_objections(1'b0);
end
@@ -244,15 +239,9 @@
if (!write) begin // read
case (channel)
AddrChannel: begin // predict at address phase
- if (tx_enabled) begin // tx enabled
- // when tx enabled and fifo full, there are 1 item in datapath in fifo
- tx_full_exp = (tx_q.size >= UART_FIFO_DEPTH + 1);
- tx_empty_exp = tx_q.size <= 1;
- end else begin // tx disabled
- tx_full_exp = tx_q.size == UART_FIFO_DEPTH;
- tx_empty_exp = tx_q.size == 0;
- end
- tx_idle_exp = tx_q.size == 0;
+ tx_full_exp = tx_q.size >= UART_FIFO_DEPTH;
+ tx_empty_exp = tx_q.size == 0;
+ tx_idle_exp = tx_q.size == 0 && tx_processing_item_q.size == 0;
rx_full_exp = rx_q.size == UART_FIFO_DEPTH;
rx_empty_exp = rx_q.size == 0;
rx_idle_exp = (uart_rx_clk_pulses == 0) || !rx_enabled;
@@ -269,37 +258,37 @@
// ignore corner case when item is about to complete, also fifo is changing from
// full to not full and it's at ignored period
- if (!(tx_enabled && tx_q_size_at_addr_ph == UART_FIFO_DEPTH + 1
+ if (!(tx_enabled && tx_q_size_at_addr_phase == UART_FIFO_DEPTH
&& is_in_ignored_period(UartTx))) begin
`DV_CHECK_EQ(get_field_val(ral.status.txfull, item.d_data), tx_full_exp, $sformatf(
"check tx_full fail: tx_en = %0d, tx_q.size = %0d, uart_tx_clk_pulses = %0d",
- tx_enabled, tx_q_size_at_addr_ph, uart_tx_clk_pulses))
+ tx_enabled, tx_q_size_at_addr_phase, uart_tx_clk_pulses))
end
if (!(rx_enabled && is_in_ignored_period(UartRx) &&
- rx_q_size_at_addr_ph inside {UART_FIFO_DEPTH - 1, UART_FIFO_DEPTH})) begin
+ rx_q_size_at_addr_phase inside {UART_FIFO_DEPTH - 1, UART_FIFO_DEPTH})) begin
`DV_CHECK_EQ(get_field_val(ral.status.rxfull, item.d_data), rx_full_exp, $sformatf(
"check rx_full fail: rx_en = %0d, rx_q.size = %0d, uart_rx_clk_pulses = %0d",
- rx_enabled, rx_q_size_at_addr_ph, uart_rx_clk_pulses))
+ rx_enabled, rx_q_size_at_addr_phase, uart_rx_clk_pulses))
end
- // if tx_q.size = 1 and tx enabled, then it's empty as the last item is being sent
- // when tx_q.size = 2 and it's at last 2 cycles, can't predict if txempty is set
- if (!(tx_enabled && tx_q_size_at_addr_ph == 2 && is_in_ignored_period(UartTx))) begin
+ // when tx_q.size = 1 and it's at last 2 cycles, can't predict if txempty is set
+ if (!(tx_enabled && tx_q_size_at_addr_phase == 1
+ && is_in_ignored_period(UartTx))) begin
`DV_CHECK_EQ(get_field_val(ral.status.txempty, item.d_data), tx_empty_exp,
$sformatf("check tx_empty fail: uart_tx_clk_pulses = %0d, tx_q.size = %0d",
- uart_tx_clk_pulses, tx_q_size_at_addr_ph))
+ uart_tx_clk_pulses, tx_q_size_at_addr_phase))
end
- if (!(rx_q_size_at_addr_ph inside {0, 1} && is_in_ignored_period(UartRx))) begin
+ if (!(rx_q_size_at_addr_phase inside {0, 1} && is_in_ignored_period(UartRx))) begin
`DV_CHECK_EQ(get_field_val(ral.status.rxempty, item.d_data), rx_empty_exp,
$sformatf("check rx_empty fail: uart_rx_clk_pulses = %0d, rx_q.size = %0d",
- uart_rx_clk_pulses, rx_q_size_at_addr_ph))
+ uart_rx_clk_pulses, rx_q_size_at_addr_phase))
end
// don't check when it's last item at last 2 cycles
- if (!(tx_q_size_at_addr_ph == 1 && is_in_ignored_period(UartTx))) begin
+ if (!(tx_q_size_at_addr_phase == 0 && is_in_ignored_period(UartTx))) begin
`DV_CHECK_EQ(get_field_val(ral.status.txidle, item.d_data), tx_idle_exp, $sformatf(
"check tx_idle fail: tx_en = %0d, tx_q.size = %0d, uart_tx_clk_pulses = %0d",
- tx_enabled, tx_q_size_at_addr_ph, uart_tx_clk_pulses))
+ tx_enabled, tx_q_size_at_addr_phase, uart_tx_clk_pulses))
end
// rx_idle_exp will be clear/set during START/STOP bit,
// but we don't use exactly same clk as DUT
@@ -316,12 +305,18 @@
end // status
"intr_state": begin
if (write && channel == AddrChannel) begin // write & address phase
- foreach (intr_exp[i]) begin
- if (item.a_data[i]) intr_exp[i] = 0;
- end
- // recalculate tx/rx watermark, will be set again if fifo size >= watermark
- predict_tx_watermark_intr();
- predict_rx_watermark_intr();
+ bit[TL_DW-1:0] intr_wdata = item.a_data;
+ fork begin
+ // add 1 cycle delay to avoid race condition when fifo changing and interrupt clearing
+ // occur simultaneously
+ cfg.clk_rst_vif.wait_clks(1);
+ intr_exp &= ~intr_wdata;
+ // recalculate tx/rx watermark, will be set again if fifo size >= watermark
+ predict_tx_watermark_intr();
+ predict_rx_watermark_intr();
+ end join_none
+ end else if (!write && channel == AddrChannel) begin // read & addr phase
+ intr_exp_at_addr_phase = intr_exp;
end else if (!write && channel == DataChannel) begin // read & data phase
uart_intr_e intr;
bit [TL_DW-1:0] intr_en = ral.intr_enable.get_mirrored_value();
@@ -341,7 +336,7 @@
continue;
end
end
- `DV_CHECK_EQ(item.d_data[i], intr_exp[i],
+ `DV_CHECK_EQ(item.d_data[i], intr_exp_at_addr_phase[i],
$sformatf("Interrupt: %0s", intr.name));
`DV_CHECK_CASE_EQ(cfg.intr_vif.pins[i], (intr_en[i] & intr_exp[i]),
$sformatf("Interrupt_pin: %0s", intr.name));
@@ -368,12 +363,7 @@
if (!write) begin
case (channel)
AddrChannel: begin // predict at address phase
- if (tx_enabled) begin
- // the ongoing item isn't included in txlvl
- txlvl_exp = tx_q.size() > 0 ? tx_q.size() - 1 : 0;
- end else begin
- txlvl_exp = tx_q.size();
- end
+ txlvl_exp = tx_q.size() > UART_FIFO_DEPTH ? UART_FIFO_DEPTH : tx_q.size();
rxlvl_exp = rx_q.size();
end
DataChannel: begin // check at data phase
@@ -455,12 +445,13 @@
uart_tx_fifo.flush();
uart_rx_fifo.flush();
tx_q.delete();
+ tx_processing_item_q.delete();
rx_q.delete();
// reset local variables
uart_tx_clk_pulses = 0;
uart_rx_clk_pulses = 0;
- tx_q_size_at_addr_ph = 0;
- rx_q_size_at_addr_ph = 0;
+ tx_q_size_at_addr_phase = 0;
+ rx_q_size_at_addr_phase = 0;
tx_enabled = ral.ctrl.tx.get_reset();
rx_enabled = ral.ctrl.rx.get_reset();
tx_full_exp = ral.status.txfull.get_reset();
@@ -480,6 +471,7 @@
`DV_EOT_PRINT_TLM_FIFO_CONTENTS(uart_item, uart_tx_fifo)
`DV_EOT_PRINT_TLM_FIFO_CONTENTS(uart_item, uart_rx_fifo)
`DV_EOT_PRINT_Q_CONTENTS(uart_item, tx_q)
+ `DV_EOT_PRINT_Q_CONTENTS(uart_item, tx_processing_item_q)
`DV_EOT_PRINT_Q_CONTENTS(uart_item, rx_q)
endfunction