caravel/verilog/dv/cocotb/interfaces/caravel.py

433 lines
20 KiB
Python

import random
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge,RisingEdge,ClockCycles
import cocotb.log
import cocotb.simulator
from cocotb.handle import SimHandleBase
from cocotb.handle import Force
from cocotb_coverage.coverage import *
from cocotb.binary import BinaryValue
import enum
from cocotb.handle import (
ConstantObject,
HierarchyArrayObject,
HierarchyObject,
ModifiableObject,
NonHierarchyIndexableObject,
SimHandle,
)
from itertools import groupby, product
import interfaces.common as common
from interfaces.common import GPIO_MODE
from interfaces.common import MASK_GPIO_CTRL
from interfaces.common import Macros
def gpio_mode(gpios_values:list):
gpios=[]
for array in gpios_values:
gpio_value = GPIO_MODE(array[1]).name
for gpio in array[0]:
gpios.append((gpio,gpio_value))
cocotb.log.info(f'[caravel][gpio_mode] gpios {gpios}')
return gpios
Carvel_Coverage = coverage_section (
CoverPoint("top.caravel.gpio", vname="gpios mode", xf = lambda gpio ,gpio_mode: (gpio,gpio_mode) ,
bins = list(product(range(38),[e.name for e in GPIO_MODE])))
)
class Caravel_env:
def __init__(self,dut:SimHandleBase):
self.dut = dut
self.clk = dut.clock_tb
self.caravel_hdl = dut.uut
self.hk_hdl = dut.uut.housekeeping
"""start carvel by insert power then reset"""
async def start_up(self):
await self.power_up()
# await self.disable_csb() # no need for this anymore as default for gpio3 is now pullup
await self.reset()
await self.disable_bins()
common.fill_macros(self.dut.macros) # get macros value
async def disable_bins(self):
for i in range(38):
common.drive_hdl(self.dut._id(f"bin{i}_en",False),(0,0),0)
"""setup the vdd and vcc power bins"""
async def power_up(self):
cocotb.log.info(f' [caravel] start powering up')
self.set_vdd(0)
self.set_vcc(0)
await ClockCycles(self.clk, 10)
cocotb.log.info(f' [caravel] power up -> connect vdd' )
self.set_vdd(1)
# await ClockCycles(self.clk, 10)
cocotb.log.info(f' [caravel] power up -> connect vcc' )
self.set_vcc(1)
await ClockCycles(self.clk, 10)
""""reset caravel"""
async def reset(self):
cocotb.log.info(f' [caravel] start resetting')
self.dut.resetb_tb.value = 0
await ClockCycles(self.clk, 20)
self.dut.resetb_tb.value = 1
await ClockCycles(self.clk, 1)
cocotb.log.info(f' [caravel] finish resetting')
def set_vdd(self,value:bool):
self.dut.vddio_tb.value = value
self.dut.vssio_tb.value = 0
self.dut.vddio_2_tb.value = value
self.dut.vssio_2_tb.value = 0
self.dut.vdda_tb.value = value
self.dut.vssa_tb.value = 0
self.dut.vdda1_tb.value = value
self.dut.vssa1_tb.value = 0
self.dut.vdda1_2_tb.value = value
self.dut.vssa1_2_tb.value = 0
self.dut.vdda2_tb.value = value
self.dut.vssa2_tb.value = 0
def set_vcc(self , value:bool):
self.dut.vccd_tb.value = value
self.dut.vssd_tb.value = 0
self.dut.vccd1_tb.value = value
self.dut.vssd1_tb.value = 0
self.dut.vccd2_tb.value = value
self.dut.vssd2_tb.value = 0
"""drive csb signal bin E8 mprj[3]"""
async def drive_csb(self,bit):
self.drive_gpio_in((3,3),bit)
self.drive_gpio_in((2,2),0)
await ClockCycles(self.clk, 1)
"""set the spi vsb signal high to disable housekeeping spi transmission bin E8 mprj[3]"""
async def disable_csb(self ):
cocotb.log.info(f' [caravel] disable housekeeping spi transmission')
await self.drive_csb(1)
"""set the spi vsb signal high impedance """
async def release_csb(self ):
cocotb.log.info(f' [caravel] release housekeeping spi transmission')
self.release_gpio(3)
self.release_gpio(2)
await ClockCycles(self.clk, 1)
"""set the spi vsb signal low to enable housekeeping spi transmission bin E8 mprj[3]"""
async def enable_csb(self ):
cocotb.log.info(f' [caravel] enable housekeeping spi transmission')
await self.drive_csb(0)
"""return the value of mprj in bits used tp monitor the output gpios value"""
def monitor_gpio(self,bits:tuple):
mprj = self.dut.mprj_io_tb.value
size =mprj.n_bits -1 #size of bins array
mprj_out= self.dut.mprj_io_tb.value[size - bits[0]:size - bits[1]]
if(mprj_out.is_resolvable):
cocotb.log.debug(f' [caravel] Monitor : mprj[{bits[0]}:{bits[1]}] = {hex(mprj_out)}')
else:
cocotb.log.debug(f' [caravel] Monitor : mprj[{bits[0]}:{bits[1]}] = {mprj_out}')
return mprj_out
"""return the value of management gpio"""
def monitor_mgmt_gpio(self):
data = self.dut.gpio_tb.value
cocotb.log.debug(f' [caravel] Monitor mgmt gpio = {data}')
return data
"""change the configration of the gpios by overwrite their defaults value then reset
need to take at least 1 cycle for reset """
### dont use back door accessing
async def configure_gpio_defaults(self,gpios_values: list):
gpio_defaults = self.caravel_hdl.gpio_defaults.value
cocotb.log.info(f' [caravel] start cofigure gpio gpios ')
size = gpio_defaults.n_bits -1 #number of bins in gpio_defaults
# list example [[(gpios),value],[(gpios),value],[(gpios),value]]
for array in gpios_values:
gpio_value = array[1]
for gpio in array[0]:
self.cov_configure_gpios(gpio,gpio_value.name)
gpio_defaults[size - (gpio*13 + 12): size -gpio*13] = gpio_value.value
#cocotb.log.info(f' [caravel] gpio_defaults[{size - (gpio*13 + 12)}:{size -gpio*13}] = {gpio_value.value} ')
self.caravel_hdl.gpio_defaults.value = gpio_defaults
#reset
self.caravel_hdl.gpio_resetn_1_shifted.value = 0
self.caravel_hdl.gpio_resetn_2_shifted.value = 0
await ClockCycles(self.clk, 1)
self.caravel_hdl.gpio_resetn_1_shifted.value = 1
self.caravel_hdl.gpio_resetn_2_shifted.value = 1
cocotb.log.info(f' [caravel] finish configuring gpios, the curret gpios value: ')
self.print_gpios_ctrl_val()
"""change the configration of the gpios by overwrite the register value
in control registers and housekeeping regs, don't consume simulation cycles"""
### dont use back door accessing
def configure_gpios_regs(self,gpios_values: list):
cocotb.log.info(f' [caravel] start cofigure gpio gpios ')
control_modules = self.control_blocks_paths()
# list example [[(gpios),value],[(gpios),value],[(gpios),value]]
for array in gpios_values:
gpio_value = array[1]
for gpio in array[0]:
self.cov_configure_gpios(gpio,gpio_value.name)
self.gpio_control_reg_write(control_modules[gpio],gpio_value.value) # for control blocks regs
self.caravel_hdl.housekeeping.gpio_configure[gpio].value = gpio_value.value # for house keeping regs
cocotb.log.info(f' [caravel] finish configuring gpios, the curret gpios value: ')
self.print_gpios_ctrl_val()
self.print_gpios_HW_val()
"""dummy function for coverage sampling"""
@Carvel_Coverage
def cov_configure_gpios(self,gpio,gpio_mode):
cocotb.log.debug(f' [caravel] gpio [{gpio}] = {gpio_mode} ')
pass
def print_gpios_default_val(self,print=1):
gpio_defaults = self.caravel_hdl.gpio_defaults.value
size = gpio_defaults.n_bits -1 #number of bins in gpio_defaults
gpios = []
for gpio in range(Macros['MPRJ_IO_PADS']):
gpio_value = gpio_defaults[size - (gpio*13 + 12): size -gpio*13]
gpio_enum = GPIO_MODE(gpio_value.integer)
gpios.append((gpio,gpio_enum))
group_bins = groupby(gpios,key=lambda x: x[1])
for key,value in group_bins:
gpios=[]
for gpio in list(value):
gpios.append(gpio[0])
if (print):
cocotb.log.info(f' [caravel] gpios[{gpios}] are {key} ')
return gpios
"""print the values return in the gpio of control block mode in GPIO Mode format"""
def print_gpios_ctrl_val(self, print=1):
control_modules = self.control_blocks_paths()
gpios = []
for i , gpio in enumerate(control_modules):
gpios.append((i,self.gpio_control_reg_read(gpio)))
group_bins = groupby(gpios,key=lambda x: x[1])
for key,value in group_bins:
gpios=[]
for gpio in list(value):
gpios.append(gpio[0])
if (print):
cocotb.log.info(f' [caravel] gpios[{gpios}] are {key} ')
return gpios
def _check_gpio_ctrl_eq_HW(self):
assert self.print_gpios_ctrl_val(1) == self.print_gpios_HW_val(1), f'there is an issue while configuration the control block register value isn\'t the same as the house keeping gpio register'
"""print the values return in the gpio of housekeeping block mode in GPIO Mode format"""
def print_gpios_HW_val(self,print=1):
gpios = []
for pin in range(Macros['MPRJ_IO_PADS']):
gpios.append((pin,GPIO_MODE(self.caravel_hdl.housekeeping.gpio_configure[pin].value)))
group_bins = groupby(gpios,key=lambda x: x[1])
for key,value in group_bins:
gpios=[]
for gpio in list(value):
gpios.append(gpio[0])
if (print):
cocotb.log.info(f' [caravel] gpios[{gpios}] are {key} ')
return gpios
"""return the paths of the control blocks"""
def control_blocks_paths(self)-> list:
car = self.caravel_hdl
control_modules =[car._id("gpio_control_bidir_1[0]",False),car._id("gpio_control_bidir_1[1]",False)]
#add gpio_control_in_1a (GPIO 2 to 7)
for i in range(6):
control_modules.append(car._id(f'gpio_control_in_1a[{i}]',False))
#add gpio_control_in_1 (GPIO 8 to 18)
for i in range(Macros['MPRJ_IO_PADS_1']-9+1):
control_modules.append(car._id(f'gpio_control_in_1[{i}]',False))
#add gpio_control_in_2 (GPIO 19 to 34)
for i in range(Macros['MPRJ_IO_PADS_2']-4+1):
control_modules.append(car._id(f'gpio_control_in_2[{i}]',False))
# Last three GPIOs (spi_sdo, flash_io2, and flash_io3) gpio_control_bidir_2
for i in range(3):
control_modules.append(car._id(f'gpio_control_bidir_2[{i}]',False))
return control_modules
"""read the control register and return a GPIO Mode it takes the path to the control reg"""
def gpio_control_reg_read(self,path:SimHandleBase) -> GPIO_MODE:
gpio_mgmt_en = path.mgmt_ena.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_MGMT_EN.value
gpio_out_dis = path.gpio_outenb.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_OUT_DIS.value
gpio_holdover = path.gpio_holdover.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_OVERRIDE.value
gpio_in_dis = path.gpio_inenb.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_INP_DIS.value
gpio_mode_sel = path.gpio_ib_mode_sel.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_MOD_SEL.value
gpio_anlg_en = path.gpio_ana_en.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_ANLG_EN.value
gpio_anlg_sel = path.gpio_ana_sel.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_ANLG_SEL.value
gpio_anlg_pol = path.gpio_ana_pol.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_ANLG_POL.value
gpio_slow_sel = path.gpio_slow_sel.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_SLOW.value
gpio_vtrip_sel = path.gpio_vtrip_sel.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_TRIP.value
gpio_dgtl_mode = path.gpio_dm.value << MASK_GPIO_CTRL.MASK_GPIO_CTRL_DGTL_MODE.value
control_reg = (gpio_mgmt_en | gpio_out_dis | gpio_holdover| gpio_in_dis | gpio_mode_sel | gpio_anlg_en
|gpio_anlg_sel|gpio_anlg_pol|gpio_slow_sel|gpio_vtrip_sel|gpio_dgtl_mode)
return(GPIO_MODE(control_reg))
"""read the control register and return a GPIO Mode it takes the path to the control reg"""
def gpio_control_reg_write(self,path:SimHandleBase,data) :
bits =common.int_to_bin_list(data,14)
path.mgmt_ena.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_MGMT_EN.value]
path.gpio_outenb.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_OUT_DIS.value]
path.gpio_holdover.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_OVERRIDE.value]
path.gpio_inenb.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_INP_DIS.value]
path.gpio_ib_mode_sel.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_MOD_SEL.value]
path.gpio_ana_en.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_ANLG_EN.value]
path.gpio_ana_sel.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_ANLG_SEL.value]
path.gpio_ana_pol.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_ANLG_POL.value]
path.gpio_slow_sel.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_SLOW.value]
path.gpio_vtrip_sel.value = bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_TRIP.value]
gpio_dm =bits[MASK_GPIO_CTRL.MASK_GPIO_CTRL_DGTL_MODE.value:MASK_GPIO_CTRL.MASK_GPIO_CTRL_DGTL_MODE.value+3]
gpio_dm =sum(d * 2**i for i, d in enumerate(gpio_dm)) # convert list to binary int
path.gpio_dm.value = gpio_dm
# """drive the value of mprj bits with spicific data from input pad at the top"""
# def release_gpio(self):
# io = self.caravel_hdl.padframe.mprj_pads.io
# mprj , n_bits = common.signal_valueZ_size(io)
# io.value = mprj
# cocotb.log.info(f' [caravel] drive_gpio_in pad mprj with {mprj}')
"""drive the value of mprj bits with spicific data from input pad at the top"""
def drive_gpio_in(self,bits,data):
# io = self.caravel_hdl.padframe.mprj_pads.io
# mprj , n_bits = common.signal_value_size(io)
# cocotb.log.debug(f' [caravel] before mprj with {mprj} and data = {data} bit [{n_bits-1-bits[0]}]:[{n_bits-1-bits[1]}]')
# mprj[n_bits-1-bits[0]:n_bits-1-bits[1]] = data
# io.value = mprj
# cocotb.log.info(f' [caravel] drive_gpio_in pad mprj with {mprj}')
data_bits = []
is_list = isinstance(bits, (list,tuple))
if is_list :
cocotb.log.debug(f'[caravel] [drive_gpio_in] start bits[1] = {bits[1]} bits[0]= {bits[0]}')
data_bits = BinaryValue(value = data, n_bits =bits[0]-bits[1]+1 ,bigEndian=(bits[0]<bits[1]))
for i,bits2 in enumerate(range(bits[1],bits[0]+1)):
self.dut._id(f"bin{bits2}",False).value = data_bits[i]
self.dut._id(f"bin{bits2}_en",False).value = 1
cocotb.log.debug(f'[caravel] [drive_gpio_in] drive bin{bits2} with {data_bits[i]} and bin{bits2}_en with 1')
else:
self.dut._id(f'bin{bits}',False).value = data
self.dut._id(f'bin{bits}_en',False).value = 1
cocotb.log.debug(f'[caravel] [drive_gpio_in] drive bin{bits} with {data} and bin{bits}_en with 1')
""" release driving the value of mprj bits """
def release_gpio(self,bits):
data_bits = []
is_list = isinstance(bits, (list,tuple))
if is_list :
cocotb.log.debug(f'[caravel] [drive_gpio_disable] start bits[1] = {bits[1]} bits[0]= {bits[0]}')
for i,bits2 in enumerate(range(bits[1],bits[0]+1)):
self.dut._id(f"bin{bits2}_en",False).value = 0
cocotb.log.debug(f'[caravel] [drive_gpio_disable] release driving bin{bits2}')
else:
self.dut._id(f'bin{bits}_en',False).value = 0
cocotb.log.debug(f'[caravel] [drive_gpio_disable] release driving bin{bits}')
"""drive the value of gpio management"""
def drive_mgmt_gpio(self,data):
mgmt_io = self.dut.gpio_tb
mgmt_io.value = data
cocotb.log.info(f' [caravel] drive_mgmt_gpio through management area mprj with {data}')
"""update the value of mprj bits with spicific data then after certain number of cycle drive z to free the signal"""
async def drive_gpio_in_with_cycles(self,bits,data,num_cycles):
self.drive_gpio_in(bits,data)
cocotb.log.info(f' [caravel] wait {num_cycles} cycles')
await cocotb.start(self.wait_then_undrive(bits,num_cycles))
cocotb.log.info(f' [caravel] finish drive_gpio_with_in_cycles ')
"""drive the value of mprj bits with spicific data from management area then after certain number of cycle drive z to free the signal"""
async def drive_mgmt_gpio_with_cycles(self,bits,data,num_cycles):
self.drive_mgmt_gpio(bits,data)
cocotb.log.info(f' [caravel] wait {num_cycles} cycles')
await cocotb.start(self.wait_then_undrive(bits,num_cycles))
cocotb.log.info(f' [caravel] finish drive_gpio_with_in_cycles ')
async def wait_then_undrive(self,bits,num_cycles):
await ClockCycles(self.clk, num_cycles)
n_bits = bits[0]-bits[1]+1
self.drive_gpio_in(bits, (n_bits)* 'z')
cocotb.log.info(f' [caravel] finish wait_then_drive ')
async def hk_write_byte(self, data):
self.path = self.dut.mprj_io_tb
data_bit = BinaryValue(value = data , n_bits = 8,bigEndian=False)
for i in range(7,-1,-1):
await FallingEdge(self.clk)
#common.drive_hdl(self.path,[(4,4),(2,2)],[0,int(data_bit[i])]) # 2 = SDI 4 = SCK
self.drive_gpio_in((2,2),int(data_bit[i]))
self.drive_gpio_in((4,4),0)
await RisingEdge(self.clk)
self.drive_gpio_in((4,4),1)
await FallingEdge(self.clk)
""" read byte using housekeeping spi
when writing to SCK we can't use mprj[4] as there is a limitation in cocotb for accessing pack array #2587
so use back door access to write the clock then read the output from the SDO mprj[1] value"""
async def hk_read_byte(self,last_read= False):
read_data =''
for i in range(8,0,-1):
self.drive_gpio_in((4,4),1)# SCK
await FallingEdge(self.clk)
self.drive_gpio_in((4,4),0)# SCK
await RisingEdge(self.clk)
read_data= f'{read_data}{self.dut.mprj_io_tb.value[37-1]}'
await FallingEdge(self.clk)
self.drive_gpio_in((4,4),0) # SCK
# if (last_read):
# common.drive_hdl(self.dut.bin4_en,(0,0),'z') #4 = SCK
# common.drive_hdl(self.path,[(1,1)],'z')
return int(read_data,2)
"""write to the house keeping registers by back door no need for commands and waiting for the data to show on mprj"""
async def hk_write_backdoor(self,addr, data):
await RisingEdge(self.dut.wb_clk_i)
self.hk_hdl.wb_stb_i.value = 1
self.hk_hdl.wb_cyc_i.value = 1
self.hk_hdl.wb_sel_i.value = 0xF
self.hk_hdl.wb_we_i.value = 1
self.hk_hdl.wb_adr_i.value = addr
self.hk_hdl.wb_dat_i.value = data
cocotb.log.info(f'Monitor: Start Writing to {hex(addr)} -> {data}')
await FallingEdge(self.dut.wb_ack_o) # wait for acknowledge
self.hk_hdl.wb_stb_i.value = 0
self.hk_hdl.wb_cyc_i.value = 0
cocotb.log.info(f'Monitor: End writing {hex(addr)} -> {data}')
"""read from the house keeping registers by back door no need for commands and waiting for the data to show on mprj"""
async def hk_read_backdoor(self,addr):
await RisingEdge(self.clk)
self.hk_hdl.wb_stb_i.value = 1
self.hk_hdl.wb_cyc_i.value = 1
self.hk_hdl.wb_sel_i.value = 0
self.hk_hdl.wb_we_i.value = 0
self.hk_hdl.wb_adr_i.value = addr
cocotb.log.info(f' [housekeeping] Monitor: Start reading from {hex(addr)}')
await FallingEdge(self.hk_hdl.wb_ack_o)
self.hk_hdl.wb_stb_i.value = 0
self.hk_hdl.wb_cyc_i.value = 0
cocotb.log.info(f' [housekeeping] Monitor: read from {hex(addr)} value {(self.hk_hdl.wb_dat_o.value)}')
return self.hk_hdl.wb_dat_o.value