During phy interface verification it mighty be handy to change pull-up (pull-down) resistors dynamically, i.e without recompiling verilog source codes and restarting simulation.
To implement this we need to assign second driver with weak strength to target wire. Depending on local variable this second driver will set logical 0 or 1.
Here is a workable systemverilog example of interface with configurable pull-ups and simple testbench with it.
Which would result in following log:
A couple of notes to mention:
1) we use enumerations to facilitate debugging, variables pull_clk and pull_data of enumeration type pull_mode_t will be shown in waveform viewer with appropriate string values (not plain int values). Moreover, we use pull_clk.name() function to print neat log messages.
2) we can't use enumerations in set_pull function, as this type can't be accessed outside of interface. So we add assertions to ensure passed int values are in enumeration range.
To implement this we need to assign second driver with weak strength to target wire. Depending on local variable this second driver will set logical 0 or 1.
Here is a workable systemverilog example of interface with configurable pull-ups and simple testbench with it.
// importing OVM here just for log messages import ovm_pkg::*; interface conf_pull_iface( inout wire clk, inout wire data); // using enumerations allows to view // enumeration names in waveform viewers instead of // integer values typedef enum { PULL_NONE=0, PULL_UP=1, PULL_DOWN=2 } pull_mode_t; pull_mode_t pull_clk = PULL_NONE; pull_mode_t pull_data = PULL_NONE; logic clk_out; bit clk_out_enable; logic data_out; bit data_out_enable; assign clk = clk_out_enable ? clk_out: 'z; assign (pull1,pull0) clk = (pull_clk == PULL_UP) ? 1'b1: ((pull_clk == PULL_DOWN) ? 1'b0: 'z); assign data = data_out_enable ? data_out : 'z; assign (pull1,pull0) data = (pull_data == PULL_UP) ? 1'b1: ((pull_data == PULL_DOWN) ? 1'b0: 'z); function void set_pull(int unsigned pull_clk_, int unsigned pull_data_); assert(pull_clk_ <= PULL_DOWN); assert(pull_data_ <= PULL_DOWN); pull_clk = pull_mode_t'(pull_clk_); pull_data = pull_mode_t'(pull_data_); ovm_report_info("", $sformatf("%m: changed: pull_clk=%s, pull_data=%s", pull_clk.name(), pull_data.name())); endfunction function void set_enable(bit clk_enable, bit data_enable); clk_out_enable = clk_enable; data_out_enable = data_enable; ovm_report_info("", $sformatf("%m: set enable: clk_enable=%s, data_enable=%s", clk_out_enable, data_out_enable)); endfunction endinterface module tb; wire clk; wire data; conf_pull_iface iface(.*); initial begin #1ns iface.set_pull(1,2); #1ns iface.set_pull(2,1); #1ns iface.set_pull(0,0); end endmodule
Using OVM-aware simulator we can run example:
$ mysimulator -ovm configur_pull_ups.sv
Which would result in following log:
OVM_INFO @ 1: reporter [] tb.iface.set_pull: changed: pull_clk=PULL_UP, pull_data=PULL_DOWN
OVM_INFO @ 2: reporter [] tb.iface.set_pull: changed: pull_clk=PULL_DOWN, pull_data=PULL_UP
OVM_INFO @ 3: reporter [] tb.iface.set_pull: changed: pull_clk=PULL_NONE, pull_data=PULL_NONE
A couple of notes to mention:
1) we use enumerations to facilitate debugging, variables pull_clk and pull_data of enumeration type pull_mode_t will be shown in waveform viewer with appropriate string values (not plain int values). Moreover, we use pull_clk.name() function to print neat log messages.
2) we can't use enumerations in set_pull function, as this type can't be accessed outside of interface. So we add assertions to ensure passed int values are in enumeration range.