noobuntu
noobuntu

Reputation: 913

Driving a internal wire in my module from my interface task

EDIT: I tried out the methods mentioned below: I set my interface to wires instead of logic, and I drive 'Z from the driver that wants to relinquish control of the signal so that the other driver can take over. Still doesn't work as I see u_slave_dut not being driven from my interface. Any clues on what is wrong? My working example: https://www.edaplayground.com/x/4SSP

I am writing a testbench for a top-level module that has a number of sub-modules. I want to instantiate an interface and hook it up to one of my sub-modules, say my_submodule. Easy enough, and I can see my interface pins toggling when my_submodule pins are toggled. This makes for great observation.

Next, I decided I wanted to be able to toggle the pins myself from my interface (using a task). I realize this leads to 2 drivers on the same bus. So, is there a way for me to do this?

I created a small example of edaplayground to experiment, and here I see that none of my writes from my task in the interface actually toggle the pins on u_slave_dut. Also, I get no warnings from the compiler which bothers me too.

My working example is here: https://www.edaplayground.com/x/5fcP

testbench.sv

`include "my_interface.sv"

module tb;

  bit clk = 1'b1;
  bit control = 1'b0;

  initial begin
    forever begin
      #5 clk = ~clk;
    end
  end

  my_interface my_vif(clk);

  assign my_vif.addr   = (control)? tb.u_top.u_slave_dut.i_addr  : 'hz;
  assign my_vif.wdata  = (control)? tb.u_top.u_slave_dut.i_wdata : 'hz;
  assign my_vif.write  = (control)? tb.u_top.u_slave_dut.i_write : 'hz;

  top u_top(.clk(clk));

  initial begin
    #80  my_vif.master_write_something;
    #160 $finish;
  end

  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0);
  end
endmodule

interface.sv

interface my_interface(input clk);
  logic [3:0] addr;
  logic       write;
  logic [3:0] wdata;
  logic [3:0] rdata;
  logic       resp;

  clocking master_cb @(posedge clk);
    input resp, rdata;
    output addr, write, wdata;
  endclocking

  clocking slave_cb @(posedge clk);
    input addr, write, wdata;
    output resp, rdata;
  endclocking

  task master_write_something;
    @(master_cb);
    master_cb.write <= 1'b1;
    @(master_cb);
    master_cb.wdata <= 3'b101;
    master_cb.addr  <= 3'b111;
    @(master_cb);
    master_cb.write <= 1'b0;
  endtask

  task slave_write_something;
    @(slave_cb);
    slave_cb.resp <= 1'b1;
    @(slave_cb);
    slave_cb.rdata <= 3'b101;
    @(slave_cb);
    slave_cb.resp <= 1'b0;
    slave_cb.rdata <= 3'b000;
  endtask

endinterface

design.sv

module slave_dut (
  input clk,
  input [3:0]  i_addr,
  input [3:0]  i_wdata,
  input        i_write,
  output       o_resp,
  output [3:0] o_rdata
);

  reg       o_resp_reg;
  reg [3:0] o_rdata_reg;

  initial begin
    o_resp_reg  <= 1'b0;
    o_rdata_reg <= 'h0;
  end

  always @(posedge clk) begin
    if (i_write == 1'b1) begin
      o_resp_reg <= 1'b1;
      o_rdata_reg <= i_wdata;
    end
    else begin
      o_resp_reg <= 1'b0;
      o_rdata_reg <= 'h0;
    end
  end

  assign o_resp = o_resp_reg;
  assign o_rdata = o_rdata_reg;

endmodule : slave_dut

module master_dut (
  input clk,
  output [3:0] o_addr,
  output [3:0] o_wdata,
  output       o_write,
  input        i_resp,
  input  [3:0] i_rdata
);

  reg [3:0] o_addr_reg;
  reg [3:0] o_wdata_reg;
  reg       o_write_reg;

  initial begin
    o_addr_reg <= 'h0;
    o_wdata_reg <= 'h0;
    o_write_reg <= 'h0;
    repeat (2) @(posedge clk);
    o_addr_reg <= 'hF;
    o_wdata_reg <= 'hB;
    o_write_reg <= 1'b1;
    @(posedge clk);
    o_addr_reg <= 'h0;
    o_wdata_reg <= 'h0;
    o_write_reg <= 'h0;
    repeat (2) @(posedge clk);
    o_addr_reg <= 'h4;
    o_wdata_reg <= 'hD;
    o_write_reg <= 1'b1;
    @(posedge clk);
    o_addr_reg <= 'h0;
    o_wdata_reg <= 'h0;
    o_write_reg <= 'h0;
  end

  assign o_addr = o_addr_reg;
  assign o_wdata = o_wdata_reg;
  assign o_write = o_write_reg;

endmodule : master_dut

module top(input clk);
  wire [3:0] addr;
  wire [3:0] wdata;
  wire       write;
  wire       resp;
  wire [3:0] rdata;

  master_dut u_master_dut (
    .clk(clk),
    .o_addr(addr),
    .o_wdata(wdata),
    .o_write(write),
    .i_resp(resp),
    .i_rdata(rdata)
  );

  slave_dut u_slave_dut (
    .clk(clk),
    .i_addr(addr),
    .i_wdata(wdata),
    .i_write(write),
    .o_resp(resp),
    .o_rdata(rdata)   
  );

endmodule

Any idea where I am going wrong?

Upvotes: 1

Views: 4930

Answers (2)

Karan Shah
Karan Shah

Reputation: 1992

As Dave explained, it is the only way to control the multi driver nets by enabling only one driver to drive the net at a time. And from hardware point of view as well, only one driver should drive any wire at a time.

This can be achieved in your design by using additional reg.

Please refer the following code.

module tb;

  bit clk = 1'b1;
  reg control = 1'b1;

  initial begin
    forever begin
      #5 clk = ~clk;
    end
  end

  my_interface my_vif(clk);

  assign my_vif.addr   = (control)? tb.u_top.u_slave_dut.i_addr  : 'hz;
  assign my_vif.wdata  = (control)? tb.u_top.u_slave_dut.i_wdata : 'hz;
  assign my_vif.write  = (control)? tb.u_top.u_slave_dut.i_write : 'hz;

  top u_top(.clk(clk));

  initial begin
    #80  my_vif.master_write_something (control);
    #160 $finish;
  end

  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0);
  end


endmodule

// Interface Task     
task master_write_something (ref reg x);
  @(master_cb);
  x = 1'b0;
  master_cb.write <= 1'b1;
  @(master_cb);
  master_cb.wdata <= 3'b101;
  master_cb.addr  <= 3'b111;
  @(master_cb);
  master_cb.write <= 1'b0;
endtask

And the dump file is as follow.

enter image description here

Upvotes: 0

dave_59
dave_59

Reputation: 42738

I believe the one simulator you chose to run the simulator on does not give any warnings or errors. Any other simulator will correctly give the errors about mixing continuous assignments with procedural assignments from the clocking block.

If you are going to have the same signal driven from two different places, you need to deal with that by using a wire instead of a variable. You also need to turn off one of the drivers by setting it to high-impedance ('z), so the other driver can control the signal. This needs to be done regardless of whether the two drivers are within your design or between the design and testbench. I wrote a DVCon paper a few years ago that explains this in more detail.

Upvotes: 1

Related Questions