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 common from common import GPIO_MODE from common import MASK_GPIO_CTRL from 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() 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): if i == 3: continue 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') await self.drive_csb('z') """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] {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