Multidriven nets: Synthesis ok, Simulation fails - simulation

I have a fundamental understanding problem with System Verilog. I am working on a processor design, where some bus systems should be shared between several processing units (System Verilog modules). With an arbiter only one module at a time should be active, driving the bus, while all other are high impedance.
I got rid of the multidriven nets warnings in Vivado during synthesis and there are not anymore any bus conflicts, but the simulator gives a warning, that the bus signals 'might' be multidriven. I made a tiny example code and I would expect to get for 'data' '11', when 'select' is '10'?
While simulation stops at all in Vivado, it works with Cadence simulator, but with wrong results - screenshot simulation
testbench.sv
`timescale 1ns / 1ps
module testbench_top();
logic [1:0] select;
logic [1:0] data;
top top_inst(.*);
initial
begin
select = 0;
#2 select = 1;
#2 select = 2;
#2 select = 0;;
end
initial
begin
$monitor("t=%3d s=%b,d=%b\n",$time,select,data);
end
endmodule
design.sv
`timescale 1ns / 1ps
module top
(
input logic [1:0] select,
output logic [1:0] data
);
driver_1 driver_1_inst(.*);
driver_2 driver_2_inst(.*);
endmodule
module driver_1
(
input logic [1:0] select,
output logic [1:0] data
);
always_comb
begin
if (select == 2'b10)
data = 2'b11;
else
data = 'z;
end
endmodule
module driver_2
(
input logic [1:0] select,
output logic [1:0] data
);
always_comb
begin
if (select == 2'b01)
data = 2'b01;
else
data = 'z;
end
endmodule

I'm assuming you expect the value of data signal the top module, which is driven by the two outputs of your driver modules, to be resolved (e.g. when one drive 'z, the other gets the bus.
This will happen if you declare the top.data signal as output wire logic [1:0] data.
Section 23.2.2.3 Rules for determining port kind, data type, and direction of the IEEE 1800-2012 standard states that
For output ports, the default port kind depends on how the data type
is specified: — If the data type is omitted or declared with the
implicit_data_type syntax, the port kind shall default to a net of
default net type. — If the data type is declared with the explicit
data_type syntax, the port kind shall default to variable.
In your case, the second clause applies, since you declared data as output logic[1:0], meaning that it was interpreted as a variable and not a net. Multiple values on variables aren't resolved (and in some tools are also illegal).

Related

Use of automatic logic variable inside a sequential procedural block--advantages?

In the following code for a simple inverter, an automatic logic variable is declared within an always_ff block and allocated using a blocking statement _GEN = ...
module Main(
input invert,
CLK,
output O0,
O1);
reg Register_inst0;
always_ff #(posedge CLK) begin
automatic logic [1:0] _GEN;
_GEN = {{~Register_inst0}, {Register_inst0}};
Register_inst0 <= _GEN[invert];
end // always_ff #(posedge)
initial
Register_inst0 = 1'h0;
wire [1:0] _GEN_0 = {{~Register_inst0}, {Register_inst0}};
assign O0 = Register_inst0;
assign O1 = _GEN_0[invert];
endmodule
Does using automatic here have advantages for the simulation / synthesized circuit performance compared to situations where the "temporary" variable is declared outside the always block? An example of the latter can be found in this answer:https://stackoverflow.com/a/62936906/5967500 and below:
logic [1:0] _GEN;
always_ff #(posedge CLK) begin
_GEN = {{~Register_inst0}, {Register_inst0}};
Register_inst0 <= _GEN[invert];
end // always_ff #(posedge)
It makes no difference for synthesis. Any variable you always write to first and then read later within an always block gets treated as combinatorial logic. That is regardless of always_ff or always_comb.
But one exception to that rule is when that variable is read from outside the always_ff, it gets treated as a register, regardless of whether the read or write comes first. Declaring a variable with an automatic lifetime is a guarantee that variable can't be read outside the always.
As far as simulation performance is concerned, that is going to be very tool dependent base on how much optimization gets applied.

Separating SystemVerilog nets before applying force

I want to force a signal down a hierarchy from my testbench. The modules are automatically created from schematics (changing design is not possible), and they are mostly based on wire type.
A code example can be found on EDA play ground.
//top level module
module dummy1(input A, output B);
dummy2 u_dummy2(A, B);
always #(A)
begin
assert (A == 1'b0) else $error("Force reached this level");
end
endmodule
module dummy2(input A, output B);
dummy3 u_dummy3(A, B);
endmodule
module dummy3(input A, output B);
assign B = A;
endmodule
If I force A in dummy3, it will change A in dummy2 and dummy1 which is something expected. I want to know if there is a way to separate A in dummy3 from A in dummy2 so that force is not applied to top level module.
module dummy_tb;
logic A, B;
dummy1 u_dummy1(A, B);
initial
begin
A = 0;
$display("step0: A=%b B=%b", u_dummy1.A,u_dummy1.B);
#1;
//force A1 to 1
force u_dummy1.u_dummy2.u_dummy3.A = 1'b1;
#1;
$display("step1: A=%b B=%b", u_dummy1.A,u_dummy1.B);
release u_dummy1.u_dummy2.u_dummy3.A;
#1;
$display("step2: A=%b B=%b", u_dummy1.A,u_dummy1.B);
//TODO: find something to separate A in u_dummy3 from A in u_dummy2, then force
end
endmodule
I can't think of a way to "separate" the nets without a change to the Verilog source code for the design modules, but you could achieve a similar effect by also forcing A in dummy2:
#1;
//force A1 to 1
force u_dummy1.u_dummy2.u_dummy3.A = 1'b1;
force u_dummy1.u_dummy2.A = 1'b0; // <-------- add this line
#1;
edaplayground
I think that this behavior results from a couple of quotes from the standard:
23.3.3 Port connection rules:
Each port connection shall be a continuous assignment of source to sink, where one connected item shall be
a signal source and the other shall be a signal sink. The assignment shall be a continuous assignment from
source to sink for input or output ports.
This makes dummy3.A the sink and dummy_tb.A the source.
10.6.2 The force and release procedural statements: A force procedural statement on a net shall override all drivers of the net—gate outputs, module outputs,
and continuous assignments—until a release procedural statement is executed on the net. When released,
the net shall immediately be assigned the value determined by the drivers of the net.
This makes dummy_tb.A a driver to the dummy3.A; So, basically verilog is forced to force all input ports on the way.
As a result, there is no way to do what you want without either forcing the port in the above level of hierarchy to a different value or to force outputs of continuous assignments in the low level module (force dummy3.B).

How to use a parameter to add or remove a signal from a system verilog interface and modport

Here is a snippet of some interface code that has some parameterized sizes to it. The fourth parameter, HAS_BURST is something I have experimented with, but it has only resulted in compilation errors.
Effectively I am looking for a way to ADD/REMOVE a signal from a interface based on parameter. Is there a way to have a generic interface with removable signals?
interface axi_if
#(parameter ID_WIDTH = 4,
ADDR_WIDTH = 40,
DATA_WIDTH = 64,
HAS_BURST = 0)
();
logic aw_ready;
logic aw_valid;
logic [ID_WIDTH-1:0] aw_bits_id;
logic [ADDR_WIDTH-1:0] aw_bits_addr;
logic [7:0] aw_bits_len;
logic [2:0] aw_bits_size;
generate
if (HAS_BURST)
logic [1:0] aw_bits_burst;
endgenerate
logic [2:0] aw_bits_size;
modport slave (
output aw_ready,
input aw_valid,
input aw_bits_id,
input aw_bits_addr,
input aw_bits_len,
generate
if (HAS_BURST)
input aw_bits_burst,
endgenerate
input aw_bits_size
);
modport master (
input aw_ready,
output aw_valid,
output aw_bits_id,
output aw_bits_addr,
output aw_bits_len,
generate
if (HAS_BURST)
output aw_bits_burst,
endgenerate
output aw_bits_size
);
endinterface
`endif
No, there isn't. Ports aren't valid in generate blocks. Parameters can be used to asjust the width of a port but not remove it entirely. You could use an `ifdef to compile it conditionally but that's an all-or-none solution. There can't be some instances with the signal and others without it.
Having the signal unconditionally present is fine in many situations and it's the easiest way to handle this problem. Tie any unused inputs to logic 0 and unused outputs can remain unconnected.
If neither of these options work there's no other way than to define two different interfaces. Doing this by hand quickly becomes unmaintainable. If there are two variations now you can be sure a third one will be needed soon, then a fourth, a fifth... Many chip design companies have SystemVerilog code generators which create customized modules for each instance.

SystemVerilog Interface in a vertical hierarchy

I'm implementing a system that uses an interface to connect two blocks that implements AXI4 lite functions.
Problem is the master and the slave blocks are inside other blocks.
The code above shows an example of what I'm trying to do:
interface bus
logic [ADDR_WIDTH-1:0] wr_addr; // Write address
logic [DATA_WIDTH-1:0] wr_data; // Write data
logic [ADDR_WIDTH-1:0] rd_addr; // Read address
logic [DATA_WIDTH-1:0] rd_data; // Read data
modport master (
output wr_addr,
output wr_data,
input rd_addr,
input rd_data
);
modport slave (
input wr_addr,
input wr_data,
output rd_addr,
output rd_data
);
endinterface
// innermost master block
module m0(bus.master bus_inst_m0);
// write/ read data from/to master modport
bus_inst_m0.wr_addr <= ...
if(bus_inst_m0.rd_addr == ...
endmodule
// instance of m0 block connected
// to interface
module m1(bus.master bus_inst_m1);
m0 m0_inst(.bus_inst_m0 (bus_inst_m1))
endmodule
// innermost slave block
module s0(bus.slave bus_inst_s0);
// write/ read data from/to slave modport
bus_inst_s0.rd_addr <= ...
if(bus_inst_s0.wd_addr == ...
endmodule
// instance of ms block connected
// to interface
module s1(bus.slave bus_inst_s1);
s0 s0_inst(.bus_inst_s0 (bus_inst_s1))
endmodule
// top module
module top();
bus local_bus;
m1 m1_inst (.bus_inst_m1 (local_bus));
s1 s1_inst (.bus_inst_s1 (local_bus));
endmodule
In Cadence Incisive simulator, the code compiles, but it not works. Looking in the schematic I see that the top interface is connected, but the inner interfaces are just open.
I made a small test connecting directly the master and slave blocks and it worked fine.
I have tried other combinations of interface/modport too, like using modport just in the innermost blocks, but it didn't work.
I read the LRM and did not found any clue about what I'm doing wrong.
Can anyone help?
Thanks
Rods
First of all, you have a bunch of options to use.
You can use generic interfaces as module parameters to avoid type errors. In this case, as long as the connected interface has all the signals that the module use, you will be okay. (And if the tool finds the proper interface...) Eg.:
module mod_gen_inf(interface gen_if);
endmodule
...
module top();
m_bus_if m_bus;
m_1 slave_actor(.gen_if(m_bus.master));
endmodule: top
Secondly, you can, but you are not required to specify the modport (type) at the module declaration. You can use the interface's name, and connect the specific modport at the module instantiation. Eg.:
module slave_actor(m_bus m_bus_if);
endmodule
...
module top();
m_bus_if m_bus;
m_1 slave_actor(.m_bus_if(m_bus.slave));
endmodule: top
Think of this as the modport is a compatible type of the given interface.
Thus a corrected/shortened version of your code:
interface m_bus;
logic [1:0] a;
modport master(input a);
modport slave (output a);
endinterface
module m1(m_bus.master bus_inst_m1);
m2 m2_inst(.bus_inst_m2(bus_inst_m1));
endmodule
module m2 (m_bus.master bus_inst_m2);
endmodule
module s1(m_bus.slave bus_inst_s1);
endmodule
module top();
m_bus local_bus();
m1 m1_inst (.bus_inst_m1 (local_bus.master));
s1 s1_inst (.bus_inst_s1 (local_bus.slave));
endmodule
This worked me fine in QuestaSim 10.5c.
If your code compiled but does not work, make sure that your innermost block sees the interface as well. (include the library that your IF is compiled into.)

Synchronous Counter

I'm trying to create a 32-bit synchronous counter using J-K flip-flops. I have a functional module for individual J-K flip-flops...
jkff(J, K, CLK, Q) where the first three are wire inputs and the last is a reg output.
I then have another functional module for the counter...
thirty_two(J, K, CLK, OUT[31:0]) where the first three are inputs and the last is output
In the thirty_two module, I instantiate many jkff modules, but I seem to be restricted to using wires as my output. Thus, OUT[31:0] is a wire instead of the desired reg I want.
Any suggestions?
A common mistake when starting out with verilog is thinking that wire & reg types have to match across hierarchy, they do not. A modules inputs are always wires and outputs can be regs or wires. Connectivity between modules are wires. The difference between usage of the two is purely down to how values are assigned or driven.
For example module thirty_two can use reg type to drive its output:
module thirty_two(
output reg [31:0] OUT
);
always #* begin
OUT = 32'bx;
end
endmodule
When instantiating thirty_two, outputs must drive wires. This make sense as the level that instantiates it can not directly change a sub modules output.
module top_level();
wire [31:0] thirty_two_out;
thirty_two thirty_two_i0 (
.OUT( thirty_two_out )
);
endmodule