Overriding imported module parameters

I am trying to use systemverilog packages, replacing include files, shared by several modules, with a package defining the various types, parameters and functions needed by module.

Often times, parameters are defined in the include files (now package) and overridden by the parent module.
Here is an example that does not work, but I do not find any reason why it could not or should not.
I do find that declaring XD and MM as parameters at the top of module nest gives the correct (overridden) operation.
But if this is the expected behavior (imported parameters are not overridable), then why not require only localparam definitions in a package ?

package axi4_pkg;
  parameter XD = 64;  // Width of data bus.
  parameter XA = 32;  // Width of address bus.
  localparam XWS = XD/8;  // Width of write strobe bus.
  // ...
endpackage

module tstpkg;
  nest #(
         .XD(256),    // This does not work - "simulator" warning issued
	 .MM(4))
    n();
endmodule

module nest ;
  import axi4_pkg::*;             // This gets XD, but not as real parameter
  parameter MM = 2;               // Explicit definition, is overridden

  initial $display("%m: XD is %0d.", XD);  // prints "XD is 64."
  initial $display("%m: MM is %0d.", MM);  // prints "MM is 4."
endmodule

In reply to Adam Krolnik:

Importing a package into a scope just makes the identifiers in the package visible to that scope; it does not move the deffiniton of the identifier (in your case a parameter) into that scope.
And if you want to have multiple instances of the module nest with different parameter values, you need to declare the parameters separately in the module.

module nest ;
  import axi4_pkg::*;
  parameter XD = axi4_pkg::XD;
  parameter MM = 2;            
 

  initial $display("%m: XD is %0d.", XD);  // prints "XD is 64."
  initial $display("%m: MM is %0d.", MM);  // prints "MM is 4."
endmodule

Now parameter XD can be overridden per instance but shares a common default value from the package.

In reply to dave_59:

You said, “Importing a package into a scope just makes the identifiers in the package visible” is thus like this:
`define XD axi4_pkg::XD;

So now in the local scope, where I refer to XD, I am really referring to the package definition.

This now gets worse. Consider the definition of localparam XWS (above). Its value has parameter dependence on XD. Since the package definition of XD is not what is being overridden in the module nest and since the imported XWS is not defined in the module nest, it retains its dependence on the package parameter values.

What this leads me to is the conclusion that packages work for sharing immutable definitions.
Is there another way to be able to use a package in the context of a parameterized design ?

In reply to Adam Krolnik:

We are heading into the XY problem territory.

I’ve seen people use a parameterized virtual class for something similar, but hard to know if this is where you want to be heading.

package p;
virtual class setp #(parameter XD = 16);
  localparam XWS = XD/8;
endclass
endpackage
                     
module top;
  import p::*;
  
  typedef setp#(.XD(256)) nest_setp;
  
  nest #(.C(nest_setp),.MM(4)) n();
endmodule

module nest;
  import p::*;
  parameter type C = setp;
  parameter MM =2;
  initial begin
    $display("%m: XD is %0d.", C::XD);
    $display("%m: MM is %0d.", MM);  
    $display("%m: XWS is %0d.", C::XWS);  
  end
endmodule