Sequence that writes to either one of two different registers, but with the same fields

108 Views Asked by At

I'm working on an old environment, that is not UVM compatible, but uses the vr_ad_reg. One of the issues with this old environment is that instead of instantiating a regfile twice for one of the modules (which in RTL is indeed instantiated twice), there is a double definition for the entire regfile and the registers in it.

For instance, I have two regfiles: "GAD" and "GAD_RX", and they both have a register defined like this:

reg_def GAD_SEQ GAD 20'h00010 {
    reg_fld s_event : uint (bits : 7) : RW : 0x0;
    reg_fld smpl_en : bit : RW : 0x0;
    reg_fld int_en : bit : RW : 0x0;
    reg_fld dma_req : bit : RW : 0x0;
    reg_fld smpl_tag : uint (bits : 2) : RW : 0x0;
    reg_fld ch_tag : uint (bits : 4) : RW : 0x0;
    reg_fld smpl_point : uint (bits : 16) : RW : 0x0;
};

reg_def GAD_RX_SEQ GAD_RX 20'h00010 {
    reg_fld s_event : uint (bits : 7) : RW : 0x0;
    reg_fld smpl_en : bit : RW : 0x0;
    reg_fld int_en : bit : RW : 0x0;
    reg_fld dma_req : bit : RW : 0x0;
    reg_fld smpl_tag : uint (bits : 2) : RW : 0x0;
    reg_fld ch_tag : uint (bits : 4) : RW : 0x0;
    reg_fld smpl_point : uint (bits : 16) : RW : 0x0;
};

As you can see, the regs are identical. I don't wish to replace the entire definition, because it is based on scripts that also generate RTL, and it's too much hassle and risk to try and rewrite them. But, I do want to be able to write a sequence that can write to both of them by constraining them to either one of the GADs.

I defined a struct member for all sequences that write to these registers:

type gad_type_t : [GAD,RX_GAD];

extend ocp_master_sequence_kind_t : [CONFIG_ADC_SEQ];

//This sequence writes a single line to GAD sequencer
extend CONFIG_ADC_SEQ ocp_master_sequence_q { 

smpl_point : int(bits:16);
dma_req : bit;
int_en : bit;
smpl_en : bit;
ch_tag : uint(bits:4);
smpl_tag : uint(bits:2);
samp_sig : uint(bits:4);
keep soft samp_sig==0;

//which GAD to config
gad_type : gad_type_t;
keep soft gad_type==GAD;

I tried declaring register variable with macro but failed completely:

//Macro for declaring either GAD or RX_GAD register variable
define <get_gad_reg'action> "gad_reg <var_name'name> : <gad_type'exp> <reg_suffix'exp>" as computed {
   var gad_regname : string;
   var gad_type : gad_type_t = <gad_type'exp>;
   var reg_suffix_s : string = <reg_suffix'exp>;
   gad_regname = gad_type == RX_GAD ? append("GAD_RX_",reg_suffix_s) : append("GAD_",reg_suffix_s);
   return append("var ",<var_name'name>," : ",gad_regname," vr_ad_reg = driver.get_reg_by_kind(",gad_regname,").as_a(",gad_regname," vr_ad_reg)");

};

Macro is supposed to be used in this way inside the body of the sequence:

gad_reg gad_seq_r : gad_type SEQ;

Which I hoped would translate to:

var gad_seq_r : GAD_SEQ vr_ad_reg = driver.get_reg_by_kind(GAD_SEQ).as_a(GAD_SEQ vr_ad_reg);

or:

var gad_seq_r : GAD_RX_SEQ vr_ad_reg = driver.get_reg_by_kind(GAD_RX_SEQ).as_a(GAD_RX_SEQ vr_ad_reg);

It worked for GAD, but not for RX_GAD, after much debugging I deduced that the gad_type isn't being matched right and the macro just takes the else clause on the conditional assignment.

I decided to try a different approach and not use macro, I tried doing this with the 'when' clause in the sequence itself:

  when GAD {
     body()@driver.clock is {
        var gad_seq_r : GAD_SEQ vr_ad_reg = driver.get_reg_by_kind(GAD_SEQ).as_a(GAD_SEQ vr_ad_reg);
    };
  };

  when RX_GAD {
     body()@driver.clock is {
        var gad_seq_r : GAD_RX_SEQ vr_ad_reg = driver.get_reg_by_kind(GAD_RX_SEQ).as_a(GAD_RX_SEQ vr_ad_reg);
     };
  };

  body()@driver.clock is also {
    gad_seq_r.smpl_point = last_line ? 0x8000 : smpl_point;
    gad_seq_r.ch_tag = ch_tag;
    gad_seq_r.smpl_tag = smpl_tag;
    gad_seq_r.dma_req = dma_req;
    gad_seq_r.int_en = int_en;
    gad_seq_r.smpl_en = smpl_en;
    gad_seq_r.s_event = s_event[6:0];
    do WR_REG seq keeping {.reg==gad_seq_r;};
};

That doesn't compile, because the compiler doesn't recognize gad_seq_r outside of 'when' clause.

I don't know if there is a solution for this outside of duplicating code for GAD and RX_GAD, but I thought I might give it a shot here. Either way, next project we will build a more reusable register database.

If you survived this far, thanks for your attention.

1

There are 1 best solutions below

1
Tudor Timi On

Since both of your registers have the exact same layout, you can just reference any one of the types inside your sequence, build up the desired value based on your sequence fields and then use write_reg <reg> value <val>:

extend CONFIG_ADC_SEQ ocp_master_sequence_q {

  gad_type : gad_type_t;

  smpl_point: int(bits:16);
  // ... all other sequence fields that model register settings

  // Used by 'write_reg'
  !gad_reg: vr_ad_reg;


  body() @driver.clock is also {
    // Dummy register variable. Could be of either GAD_SEQ or GAD_RX_SEQ type
    var dummy_gad_reg: GAD_SEQ vr_ad_reg;

    dummy_gad_reg.smpl_point = smpl_point;
    // ... set all other fields of 'gad_seq_r' based on your sequence fields

    // Get a pointer to the "real" GAD reg you're trying to access
    var real_gad_reg: vr_ad_reg;
    if gad_type == GAD {
      real_gad_reg = driver.get_reg_by_kind(GAD_SEQ);
    }
    else {
      real_gad_reg = driver.get_reg_by_kind(GAD_RX_SEQ);
    };

    // Instruct 'vr_ad' to write the value in your dummy reg to the real reg
    write_reg gad_reg { .static_item == real_gad_reg }
        val dummy_gad_reg.get_cur_value();
  };

};

I noticed you're using some WR_REG sequence to write your registers, which takes the register to be written as an argument. If using write_reg isn't an option (for whatever reason) and you have to use your sequence, I think you could replace the call to write_reg from above with:

real_gad_reg.write_reg_val(dummy_gad_reg.get_cur_value());
do WR_REG seq keeping {
  .reg == real_gad_reg;
};