Skip to content

Commit

Permalink
Add coverage and update the ref model
Browse files Browse the repository at this point in the history
  • Loading branch information
M0stafaRady committed Apr 29, 2024
1 parent 77bbd92 commit f779587
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 14 deletions.
2 changes: 1 addition & 1 deletion EF_SPI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ registers:
bit_offset: 0
bit_width: 1
write_port: ""
description: ""
description: "self clear field"
- name: SSn
bit_offset: 1
bit_width: 1
Expand Down
14 changes: 12 additions & 2 deletions hdl/rtl/bus_wrappers/APB_SPI.v
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,20 @@ module EF_SPI_APB(

`APB_SPI_REG(DATA_REG, 8, 0, DATA_REG_sel, DATA_REG_OFF)
`APB_SPI_REG(CFG_REG, 2, 0, CFG_REG_sel, CFG_REG_OFF)
`APB_SPI_REG(CTRL_REG, 2, 0, CTRL_REG_sel, CTRL_REG_OFF)
// `APB_SPI_REG(CTRL_REG, 2, 0, CTRL_REG_sel, CTRL_REG_OFF)
`APB_SPI_REG(PRE_REG, 8, 2, PRE_REG_sel, PRE_REG_OFF)
`APB_SPI_REG(IM_REG, 1, 0, IM_REG_sel, IM_REG_OFF)

// make CTRL_REG[0] self clear register
reg [1:0] CTRL_REG;
wire CTRL_REG_sel = PENABLE & PWRITE & PREADY & PSEL & (PADDR[7:3] == CTRL_REG_OFF);
always @(posedge PCLK or negedge PRESETn)
if (~PRESETn)
CTRL_REG <= 0;
else if (CTRL_REG_sel)
CTRL_REG <= PWDATA[1:0];
else
CTRL_REG[0] <= 1'b0;

reg IC_REG;
wire IC_REG_sel = (PENABLE & PWRITE & PREADY & PSEL & (PADDR[7:3] == IC_REG_OFF));
always @(posedge PCLK, negedge PRESETn)
Expand Down
58 changes: 58 additions & 0 deletions verify/uvm-python/spi_coverage/ip_cov_groups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from uvm.macros.uvm_message_defines import uvm_info
from uvm.base.uvm_object_globals import UVM_HIGH, UVM_LOW
from cocotb_coverage.coverage import CoverPoint, CoverCross
from uvm.macros import uvm_component_utils


class ip_cov_groups:
def __init__(self, hierarchy, regs) -> None:
self.hierarchy = hierarchy
self.regs = regs
self.ip_cov(None, do_sampling=False)
self.item_width = 8

def ip_cov(self, tr, do_sampling=True):
@CoverPoint(
f"{self.hierarchy}.MISO",
xf=lambda tr: tr.MISO,
bins=[(0, 0xF), (0x10, 0xF0), (0xF1, 0xFF)],
at_least=3,
rel=lambda val, b: b[0] <= val <= b[1],
)
@CoverPoint(
f"{self.hierarchy}.MOSI",
xf=lambda tr: tr.MOSI,
bins=[(0, 0xF), (0x10, 0xF0), (0xF1, 0xFF)],
at_least=3,
rel=lambda val, b: b[0] <= val <= b[1],
)
@CoverPoint(
f"{self.hierarchy}.clock_polarity",
xf=lambda tr: self.regs.read_reg_value("CFG") & 0b1,
bins=[False, True],
bins_labels=["normal", "inverted"],
at_least=3,
)
@CoverPoint(
f"{self.hierarchy}.clock_phase",
xf=lambda tr: (self.regs.read_reg_value("CFG") & 0b10) >> 1,
bins=[False, True],
bins_labels=["normal", "shifted90"],
at_least=3,
)
@CoverPoint(
f"{self.hierarchy}.PRESCALER",
xf=lambda tr: self.regs.read_reg_value("PR"),
bins=[(0, 0x5), (0x5, 0xFFFF)],
at_least=3,
rel=lambda val, b: b[0] <= val <= b[1],
)
@CoverCross(
f"{self.hierarchy}.clock_polarity_phase",
items=[f"{self.hierarchy}.clock_polarity", f"{self.hierarchy}.clock_phase"],
)
def sample(tr):
uvm_info("coverage_ip", f"tr = {tr}", UVM_LOW)

if do_sampling:
sample(tr)
11 changes: 8 additions & 3 deletions verify/uvm-python/spi_coverage/spi_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from uvm.base.uvm_config_db import UVMConfigDb
from uvm.macros.uvm_tlm_defines import uvm_analysis_imp_decl
from EF_UVM.ip_env.ip_coverage.ip_coverage import ip_coverage
from spi_coverage.ip_cov_groups import ip_cov_groups


class spi_coverage(ip_coverage):
Expand All @@ -19,11 +20,15 @@ def __init__(self, name="spi_coverage", parent=None):
def build_phase(self, phase):
super().build_phase(phase)
# TODO: initialize the class for coverage groups here
arr = []
if not UVMConfigDb.get(self, "", "bus_regs", arr):
uvm_fatal(self.tag, "No json file wrapper regs")
else:
regs = arr[0]
self.cov_groups = ip_cov_groups("top.ip", regs)

def write(self, tr):
# called when new transaction from ip monitor is received
# TODO: Add sampling logic here
pass
self.cov_groups.ip_cov(tr)


uvm_component_utils(spi_coverage)
5 changes: 4 additions & 1 deletion verify/uvm-python/spi_ref_model/spi_ref_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ def write_bus(self, tr):
# For example, to read the same resgiter uncomment the following lines
data = self.regs.read_reg_value(tr.addr)
td = tr.do_clone()
td.data = data
if tr.addr == self.regs.reg_name_to_address["STATUS"]:
pass # don't change the data as the status register isnt calculated in the ref model for now
else:
td.data = data
self.bus_bus_export.write(td) # this is output to the scoreboard
self.update_interrupt_regs()

Expand Down
14 changes: 8 additions & 6 deletions verify/uvm-python/spi_seq_lib/spi_bus_seq.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,20 @@ async def body(self):
await self.send_req(
is_write=True, reg="CTRL", data_condition=lambda data: data == 0b11
)

for _ in range(50):
await self.send_nop()
await self.send_req(
is_write=True, reg="CTRL", data_condition=lambda data: data == 0b11
)
for _ in range(50):
await self.send_nop()

await self.send_req(
is_write=True, reg="PR", data_condition=lambda data: data > 2 and data < 10
)

for _ in range(500):
for _ in range(300):
await self.send_nop()
await self.send_req(
is_write=True, reg="CTRL", data_condition=lambda data: data == 0b11
)
for _ in range(200):
await self.send_nop()


Expand Down
61 changes: 61 additions & 0 deletions verify/uvm-python/spi_seq_lib/spi_send_MOSI_seq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from uvm.seq import UVMSequence
from uvm.macros.uvm_object_defines import uvm_object_utils
from uvm.macros.uvm_message_defines import uvm_fatal
from uvm.base.uvm_config_db import UVMConfigDb
from EF_UVM.bus_env.bus_seq_lib.bus_seq_base import bus_seq_base
from cocotb.triggers import Timer
from uvm.macros.uvm_sequence_defines import uvm_do_with, uvm_do
from uvm.base.uvm_object_globals import UVM_ALL_ON, UVM_NOPACK, UVM_HIGH, UVM_MEDIUM
from uvm.macros import uvm_component_utils, uvm_fatal, uvm_info


class spi_send_MOSI_seq(bus_seq_base):
# use this sequence write or read from register by the bus interface
# this sequence should be connected to the bus sequencer in the testbench
# you should create as many sequences as you need not only this one
def __init__(self, name="spi_send_MOSI_seq", num_data=10, data_width=8):
super().__init__(name)
self.num_data = num_data
self.data_width = data_width
regs_arr = []
if not UVMConfigDb.get(self, "", "bus_regs", regs_arr):
uvm_fatal(self.tag, "No json file wrapper regs")
else:
self.regs = regs_arr[0]

async def body(self):
await super().body()
# Add the sequqnce here
# you could use method send_req to send a write or read using the register name
# example for writing register by value > 5
await self.send_req(
is_write=True, reg="CTRL", data_condition=lambda data: data == 0b10
)
for _ in range(self.num_data):
await self.send_req(
is_write=True,
reg="DATA",
data_condition=lambda data: data < (1 << self.data_width) - 1,
)
await self.send_req(
is_write=True, reg="CTRL", data_condition=lambda data: data == 0b11
) # go
while True:
await self.send_req(is_write=False, reg="STATUS")
# pop non needed response in the fifo
while True:
rsp = []
await self.get_response(rsp)
rsp = rsp[0]
uvm_info(self.get_full_name(), f"RSP: {rsp}", UVM_MEDIUM)
if rsp.addr == self.regs.reg_name_to_address["STATUS"]:
break
if rsp.data & 0b10 == 0b0: # not busy
break

await self.send_req(
is_write=True, reg="CTRL", data_condition=lambda data: data == 0b00
) # csb disable


uvm_object_utils(spi_send_MOSI_seq)
3 changes: 2 additions & 1 deletion verify/uvm-python/test_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# seqences import
from spi_seq_lib.spi_bus_seq import spi_bus_seq
from spi_seq_lib.spi_ip_seq import spi_ip_seq
from spi_seq_lib.spi_send_MOSI_seq import spi_send_MOSI_seq
from spi_seq_lib.configure_spi_seq import configure_spi_seq

# override classes
Expand Down Expand Up @@ -109,7 +110,7 @@ async def main_phase(self, phase):
phase.raise_objection(self, f"{self.__class__.__name__} OBJECTED")
# TODO: conntect sequence with sequencer here
# for example if you need to run the 2 sequence sequentially
bus_seq = spi_bus_seq("spi_bus_seq")
bus_seq = spi_send_MOSI_seq("spi_send_MOSI_seq")
ip_seq = spi_ip_seq("spi_ip_seq")
await cocotb.start(ip_seq.start(self.ip_sqr))
await bus_seq.start(self.bus_sqr)
Expand Down

0 comments on commit f779587

Please sign in to comment.