How to do a parametrizable access to a bit slice

Hi,

I’d like to be able to do access to bit slice that are parameterizable.
Ideally I 'd like to use table containing MSB / LSB or size/ offset, e.g.:

const size_offset_t offset_table[string] = '{
                                             "CTRL_RD_WR" : '{ size:4, offset:0},
                                             "CTRL_RELOAD_MASK" : '{ size:1, offset:4},
                                             "CTRL_SHADOW_UPDATE_MASK" : '{ size:1, offset:5},
                                             "CTRL_STARTUP_CLK_MASK" : '{size:1, offset:6}
                                             };

const lsb_msb_t bit_table[string] = '{
                                      "CTRL_RD_WR" : '{ msb:4, lsb:0},
                                      "CTRL_RELOAD_MASK" : '{ msb:5, lsb:4},
                                      "CTRL_SHADOW_UPDATE_MASK" : '{ msb:6, lsb:5},
                                      "CTRL_STARTUP_CLK_MASK" : '{msb:7, lsb:6}
                                      };

However, when trying to implement the following code:

   function bit[31:0] assign_value (
                                    input bit [31:0] initial_value,
                                    input bit [31:0] assigned_field,
                                    const ref int        lsb,
                                    const ref int        msb);

      assign_value = initial_value;
      assign_value[msb:lsb] = assigned_field;
      
   endfunction : assign_value

I get following compilation error whether I try to compile under Synopsys or Cadence (do not have access to Mentor Questa)…
**assign_value[msb:lsb] = assigned_field;
|
xmvlog: *E,NOTPAR (slice_access.sv,86|21): Illegal operand for constant expression [4(IEEE)].
**

Does anyone know can I work around this problem?

In reply to sylvainb:

In SystemVerilog, a const is not the same thing as a constant. A const is still a variable that is written only once at the time of the variable’s initialization.

You will need to create a for loop that does the assignment bit for bit.

Hi Dave,

Thank you for your answer, I was actually looking for a solution that was more elegant and more “SystemVerilog” than using C style for assigning a slice…

By C Style I mean:

`define CTRL_RD_WR_MASK                  'hF
`define CTRL_RD_WR_READ_FUSE             'h1
rd_data = (rd_data & ~`CTRL_RD_WR_MASK ) | ( `CTRL_RD_WR_READ_FUSE & `CTRL_RD_WR_MASK);

By SystemVerilog style I was hoping using the associative arrays of const int defined in my 1st post and the expression:

rd_data[offset_table["CTRL_RD_WR"].offset+:offset_table["CTRL_RD_WR"].size] = CTRL_RD_WR_READ_FUSE;

The only solution I see now, if I want to avoid the loop for assigning every bit one by one is to use of preprocessor `define values or (local)parameters… Unfortunately I cannot pack those into associative arrays (unlike const int).

In reply to sylvainb:

If you want to avoid the loop for assigning every bit one by one.

function bit[31:0] assign_value (
                                    input bit [31:0] initial_value,
                                    input bit [31:0] assigned_field,
                                     int        lsb,
                                     int        msb);
      int tmp;
      assign_value = initial_value;
      
      tmp  = ((1<<(msb-lsb+1))-1) << lsb;
      assign_value = (assign_value & ~tmp) | assigned_field << lsb;
  
endfunction : assign_value

In reply to Rahulkumar Patel:

Hi,

That’s exactly what I meant by C-style bit slice assignment, using an inverted mask and logical ‘or’. The thread was triggered from a discussion we had within our team about whether there was a better way of doing bit slice assignment in SystemVerilog - most of people use indeed C-Style assignment.
C-style assignment works perfectly but it’s far less readable than:
vector[msb:lsb]=new_slice_value;
Unfortunately this previous statement only works when msb and lsb are constant (not const int as documented by Dave previously).

In reply to sylvainb:
This seems very readable to me:

function bit[31:0] assign_value (
                                    bit [31:0] initial_value,
                                    bit [31:0] assigned_field,
                                    int        lsb,
                                    int        msb);
 
      assign_value = initial_value;
      for(int index=lsb; index <= msb; index++) assign_value[index] = assigned_field[index-lsb];
endfunction : assign_value

In reply to dave_59:

Hi Dave,

I agree with you that it is readable but it forces everyone in the project to use this ‘assign_value’ function in order to circumvent the requirement of SystemVerilog demanding constant for msb_expr and lsb_expr (or for width_expr in case of part-select addressing) cf. IEEE 1800-2017 section 11.5.1
It then becomes harder to convince people using C-style bit slice access to switch to a function locally defined in the project…

This assign_value function begs the question of, why does this restriction still exist in the language?

In reply to warnerrs:

This assign_value function begs the question of, why does this restriction still exist in the language?

This is because SystemVerilog is a statically typed language and bit-width is a part of the type. You cannot have an operand dynamically changing its type. It may seem overly restrictive for a simple assignment, but the rules for evaluating bit-widths of expressions gets complex quickly as soon as you introduce boolean and arithmetic expression involving operands of different bit-widths with signed and unsigned types.

So if I summarize, the msb_exp and lsb_exp, for defining the slice, must be either:

  • plain number
  • preprocessor macro pointing to a plain number (`define)
  • (local) parameter
  • enumeration name

And unfortunately const int that offers a lot of flexibility in term of being packaged into const (associative) arrays is not an option…

In reply to sylvainb:

Correct. And don’t forget you can have a constant width slice with a variable index using [index+:width] or [index-:width].