2022-10-10 06:50:45 -05:00
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 ( )
2022-10-11 10:30:02 -05:00
await self . disable_csb ( ) # no need for this anymore as default for gpio3 is now pullup
2022-10-10 06:50:45 -05:00
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 ) :
2022-10-11 10:30:02 -05:00
if i == 3 :
continue
2022-10-10 06:50:45 -05:00
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