@@ -70,7 +70,7 @@ def write(self, file, db : UCIS, ctx=None):
7070 self .setAttr (self .root , "writtenBy" , wb if wb else getpass .getuser ())
7171 wt = db .getWrittenTime ()
7272 if wt :
73- self .setAttrDateTime (self .root , "writtenTime" , str ( wt ) )
73+ self .setAttrDateTime (self .root , "writtenTime" , wt )
7474 else :
7575 self .setAttrDateTime (self .root , "writtenTime" ,
7676 date .today ().strftime ("%Y%m%d%H%M%S" ))
@@ -208,15 +208,7 @@ def write_instance_coverages(self, s, parent_instance_id=None):
208208 self .write_branch_coverage (inst , s )
209209 self .write_fsm_coverage (inst , s )
210210 self .write_assertion_coverage (inst , s )
211- # Warn once per instance if condition/expression scopes are present
212- warned = False
213- for _ in s .scopes (ScopeTypeT .COND ):
214- if not warned and self .ctx is not None :
215- self .ctx .warn (
216- "xml: condition/expression coverage is not supported "
217- "— scopes skipped" )
218- warned = True
219- break
211+ self .write_condition_coverage (inst , s )
220212 self .write_user_attrs (inst , s )
221213
222214 # Recursively write child instances
@@ -241,7 +233,10 @@ def write_toggle_coverage(self, inst_elem, scope):
241233 tb_elem .set ("key" , "0" )
242234 for bin_item in bins :
243235 name = bin_item .getName ()
244- if "to" in name .lower ():
236+ if "->" in name :
237+ parts = name .split ("->" , 1 )
238+ from_val , to_val = parts [0 ].strip (), parts [1 ].strip ()
239+ elif "to" in name .lower ():
245240 parts = name .lower ().split ("to" , 1 )
246241 from_val , to_val = parts [0 ], parts [1 ]
247242 else :
@@ -300,27 +295,31 @@ def write_fsm_coverage(self, inst_elem, scope):
300295 fsm_elem .set ("name" , fsm_scope .getScopeName ())
301296 fsm_elem .set ("type" , "reg" )
302297 fsm_elem .set ("width" , "1" )
303- bins = list (fsm_scope .coverItems (CoverTypeT .FSMBIN ))
298+ # FSMBIN items live in FSM_STATES and FSM_TRANS child scopes (LRM 6.5.6)
299+ state_bins = []
300+ for ss in fsm_scope .scopes (ScopeTypeT .FSM_STATES ):
301+ state_bins .extend (ss .coverItems (CoverTypeT .FSMBIN ))
302+ trans_bins = []
303+ for ts in fsm_scope .scopes (ScopeTypeT .FSM_TRANS ):
304+ trans_bins .extend (ts .coverItems (CoverTypeT .FSMBIN ))
304305 # States must come before transitions in XML (schema order)
305- for bin_item in bins :
306- if "->" not in bin_item .getName ():
307- state_elem = self .mkElem (fsm_elem , "state" )
308- state_elem .set ("stateName" , bin_item .getName ())
309- state_elem .set ("stateValue" , str (bins .index (bin_item )))
310- sb_elem = self .mkElem (state_elem , "stateBin" )
311- contents = self .mkElem (sb_elem , "contents" )
312- contents .set ("coverageCount" , str (bin_item .getCoverData ().data ))
313- for bin_item in bins :
314- if "->" in bin_item .getName ():
315- parts = bin_item .getName ().split ("->" , 1 )
316- trans_elem = self .mkElem (fsm_elem , "stateTransition" )
317- from_elem = self .mkElem (trans_elem , "state" )
318- from_elem .text = parts [0 ]
319- to_elem = self .mkElem (trans_elem , "state" )
320- to_elem .text = parts [1 ]
321- tb_elem = self .mkElem (trans_elem , "transitionBin" )
322- contents = self .mkElem (tb_elem , "contents" )
323- contents .set ("coverageCount" , str (bin_item .getCoverData ().data ))
306+ for i , bin_item in enumerate (state_bins ):
307+ state_elem = self .mkElem (fsm_elem , "state" )
308+ state_elem .set ("stateName" , bin_item .getName ())
309+ state_elem .set ("stateValue" , str (i ))
310+ sb_elem = self .mkElem (state_elem , "stateBin" )
311+ contents = self .mkElem (sb_elem , "contents" )
312+ contents .set ("coverageCount" , str (bin_item .getCoverData ().data ))
313+ for bin_item in trans_bins :
314+ parts = bin_item .getName ().split ("->" , 1 )
315+ trans_elem = self .mkElem (fsm_elem , "stateTransition" )
316+ from_elem = self .mkElem (trans_elem , "state" )
317+ from_elem .text = parts [0 ].strip () if len (parts ) > 1 else bin_item .getName ()
318+ to_elem = self .mkElem (trans_elem , "state" )
319+ to_elem .text = parts [1 ].strip () if len (parts ) > 1 else ""
320+ tb_elem = self .mkElem (trans_elem , "transitionBin" )
321+ contents = self .mkElem (tb_elem , "contents" )
322+ contents .set ("coverageCount" , str (bin_item .getCoverData ().data ))
324323 self .write_user_attrs (fc_elem , scope )
325324
326325 def write_assertion_coverage (self , inst_elem , scope ):
@@ -360,6 +359,44 @@ def write_assertion_coverage(self, inst_elem, scope):
360359 str (sum (b .getCoverData ().data for b in bins )))
361360 self .write_user_attrs (ac_elem , scope )
362361
362+ def write_condition_coverage (self , inst_elem , scope ):
363+ cond_scopes = list (scope .scopes (ScopeTypeT .COND ))
364+ if not cond_scopes :
365+ return
366+ cc_elem = self .mkElem (inst_elem , "conditionCoverage" )
367+ cc_elem .set ("metricMode" , "UCIS:STD" )
368+ for key , cond_scope in enumerate (cond_scopes ):
369+ src = cond_scope .getSourceInfo ()
370+ if src and src .file :
371+ file_id = self .file_id_m .get (src .file .getFileName (), 1 )
372+ line = src .line if src .line >= 1 else 1
373+ else :
374+ file_id = 1
375+ line = 1
376+ uid = f"#cond#{ file_id } #{ line } #{ key } #"
377+ expr_elem = self .mkElem (cc_elem , "expr" )
378+ expr_elem .set ("name" , uid )
379+ expr_elem .set ("key" , str (key ))
380+ expr_elem .set ("exprString" , cond_scope .getScopeName ())
381+ expr_elem .set ("index" , "0" )
382+ expr_elem .set ("width" , "1" )
383+ self .addId (expr_elem , src )
384+ bins = list (cond_scope .coverItems (CoverTypeT .CONDBIN ))
385+ # Infer input count from max length of binary-vector bin names
386+ binary_names = [b .getName () for b in bins
387+ if all (c in '01-' for c in b .getName ())]
388+ n_inputs = max ((len (n ) for n in binary_names ), default = 1 ) if binary_names else 1
389+ # Emit placeholder subExpr names (signal names not available in DM)
390+ for i in range (n_inputs ):
391+ sub_elem = self .mkElem (expr_elem , "subExpr" )
392+ sub_elem .text = f"_input_{ i } "
393+ for bin_item in bins :
394+ bin_elem = self .mkElem (expr_elem , "bin" )
395+ bin_elem .set ("alias" , bin_item .getName ())
396+ contents_elem = self .mkElem (bin_elem , "contents" )
397+ contents_elem .set ("coverageCount" , str (bin_item .getCoverData ().data ))
398+ self .write_user_attrs (cc_elem , scope )
399+
363400 def write_covergroups (self , inst , scope ):
364401 for cg in scope .scopes (ScopeTypeT .COVERGROUP ):
365402 cgElem = self .mkElem (inst , "covergroupCoverage" )
@@ -425,14 +462,15 @@ def write_coverpoint_bins(self, cpElem, coveritems : Iterator[CoverIndex]):
425462 cpBinElem = self .mkElem (cpElem , "coverpointBin" )
426463 self .setAttr (cpBinElem , "name" , cp_bin .getName ())
427464
428- if cp_bin .data .type == CoverTypeT .CVGBIN :
465+ bin_type = cp_bin .getCoverData ().type
466+ if bin_type == CoverTypeT .CVGBIN :
429467 self .setAttr (cpBinElem , "type" , "bins" )
430- elif cp_bin . data . type == CoverTypeT .IGNOREBIN :
468+ elif bin_type == CoverTypeT .IGNOREBIN :
431469 self .setAttr (cpBinElem , "type" , "ignore" )
432- elif cp_bin . data . type == CoverTypeT .ILLEGALBIN :
470+ elif bin_type == CoverTypeT .ILLEGALBIN :
433471 self .setAttr (cpBinElem , "type" , "illegal" )
434472 else :
435- raise Exception ("Unknown bin type %s" % str (cp_bin . type ))
473+ raise Exception ("Unknown bin type %s" % str (bin_type ))
436474 self .setAttr (cpBinElem , "key" , "0" )
437475
438476 # Now, add the coverage data
0 commit comments