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

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.

Related

passing generated modports to instances of the same module

I'm pretty sure there is no way to do what I am trying, but just in case there is an interesting clever solution, I thought I'd ask around. I have a parameterized SystemVerilog interface, inside of which I am generating modports
interface some_interface #(parameter NUM_READERS=3);
logic [`kBitsPerProgramCounter-1:0] read_addr[NUM_READERS];
logic [`kBitsPerProgramCounter-1:0] write_addr;
genvar i;
generate
// generates Reader[n].Modport
for (i = 0; i < NUM_READERS; ++i) begin : Reader
modport Modport
(
output .read_addr(read_addr[i])
);
end
endgenerate
endinterface
Using this is easy if I have different module types taking different modports. However, what I wanted to try doing is to have multiple instances of the same module. I tried this by parameterizing on type
module some_block#(parameter type SOMETYPE) (
input logic CLK200MHZ,
SOMETYPE aarrgghh);
But getting it to work syntactically has been challenging. Giving SOMETYPE a default value doesn't work because Vivado complains about not allowing hierarchical types, so right off the bat I don't think this will work. When instantiating, I tried using the full modport name, the full modport name with the instantiated interface, and a few others, but nothing seems to work.
So I am curious if there is a clever way I can have multiple instantiations of some_block, each taking a different generated modport. And if not, is there some fun clever trick I can do to use the generated modports? The idea in the first place was that I have a thing that has one writer and multiple readers. I wanted to generate a modport for each reader so that it could only touch it's own read address. I guess I could always just parameterize some_block on an integer, pass some_block the whole interface, and then rely on some_block to only touch the read address corresponding to the passed in integer, but that might be error prone.
Assuming that 'generate' works, there is nothing to be concerned about modules. There is no need to pass a type parameter. The module port is just supposed to be of the type of your interface.
module top();
some_interface ifc;
for (genvar gi = 0; gi < NUM_REASDERS; gi++) begin: inst
some_block sb(ifc.Reader[gi].Modport);
end
endmodule
module some_block (some_interface ifc);
always_comb myvar = ifc.read_addr;
some_block just always references the 'read_addr' which is the modport var. You can use a generate block in the 'top' module.

Multidriven nets: Synthesis ok, Simulation fails

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).

How do I sign extend in SystemVerilog?

Below is the code I have for my module:
module sext(input in[3:0], output out[7:0]);
always_comb
begin
if(in[3]==1'b0)
assign out = {4'b0000,in};
else
assign out = {4'b1111,in};
end
endmodule
For some reason this is not working. Instead of sign extending it is zero extending. Any ideas to why this might be the case?
I'm going to assume you meant (input [3:0] in, output [7:0] out). If that is true, then all you needed to write is
module sext(input signed [3:0] in, output signed [7:0] out);
assign out = in;
endmodule
You could also write
module sext(input [3:0] in, output [7:0] out);
assign out = 8'(signed'(in));
endmodule
And perhaps you don't even need to write this as a separate module.
Few things you need to take care is,
you haven't declared a data type for in and out, so by default they are wire and wire can't be used at LHS inside procedural block. Refer Section 6.5 Nets and variables (SV LRM 1800-2012). So either use a continuous assignment or declare it as a variable (i.e. reg/logic etc.).
The assignment of unpacked array is illegal in your example, so either use packed array or follow the instructions given in Section 10.10 Unpacked array concatenation (SV LRM 1800-2012)
It is not illegal syntax but assign used inside an always block probably does not do what you think it does. Use assign for wires and do not use it inside initial or always.
You have defined your port ranges after the name, this results in 4 and 8 1-bit arrays rather than a 4 and 8 bit value.
You have used {} for concatination, but they can also be used for replication ie {4{1'b1}}.
module sext(
input [3:0] in,
output reg [7:0] out ); //ranged defined before name
//No assign in always
//concatenation with replication
always_comb begin
out = { {4{in[3]}}, in};
end
endmodule
Or :
module sext(
input [3:0] in,
output [7:0] out ); //out left as wire
assign out = { {4{in[3]}}, in};
endmodule
I have seen your code.
There are some mistake in your code that you have to take care whiling writing the code.
You have use unpacked array so your targeted elements and actual elements are not match.
ERROR : Number of elements in target expression does not match the number of
elements in source expression.
This error can solve by using packed array.So, your targeted elements and actual elements are matched.
Here is link from where you will get better understanding regarding packed and unpacked array.
LINK : [http://www.testbench.in/SV_09_ARRAYS.html][1]
2.Another thing that you have to take care is you are storing some value in out signal(variable) like assign out = {4'b0000,in};
So you have to use reg data type to sore the value.
ERROR : Non reg type is not valid on the left hand side of this assignment
When you use reg data type then you can store value in out data type.
So, your problem is solved.
Here I also provide code which will run fine.
module sext(input [3:0]in, output reg [7:0]out);
always_comb
begin
if(in[3]==1'b0)
assign out = {4'b0000,in};
else
assign out = {4'b1111,in};
end
endmodule

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

Difference of SystemVerilog data types (reg, logic, bit)

There are different data types in SystemVerilog that can be used like the following:
reg [31:0] data;
logic [31:0] data;
bit [31:0] data;
How do the three of them differ?
reg and wire were the original types. Wires are constantly assigned and regs are evaluated at particular points, the advantage here is for the simulator to make optimisations.
wire w_data;
assign w_data = y;
// Same function as above using reg
reg r_data;
always #*
r_data = y ;
A common mistake when learning Verilog is to assume the a reg type implies a register in hardware. The earlier optimisation for the simulator can be done through the context of its usage.
This introduces logic which can be used in place of wire and reg.
logic w_data;
assign w_data = y;
// Same function as above using reg
logic r_data;
always #*
r_data = y ;
The type bit and byte have also been created that can only hold 2 states 0 or 1 no x or z. byte implies bit [7:0]. Using these types offers a small speed improvement but I would recommend not using them in RTL as your verification may miss uninitialized values or critical resets.
The usage of bit and byte would be more common in testbench components, but can lead to issues in case of having to drive x's to stimulate data corruption and recovery.
Update
At the time of writing I was under the impression that logic could not be used for tristate, I am unable to find the original paper that I based this on. Until further updates, comments or edits, I revoke my assertion that logic can not be used to create tri-state lines.
The tri type has been added, for explicitly defining a tri-state line. It is based on the properties of a wire, logic is based on the properties of a reg.
tri t_data;
assign t_data = (drive) ? y : 1'bz ;
If you no longer have to support backwards compatibility Verilog then I would recommend switching to using logic and tri. Using logic aids re-factoring and and tri reflects the design intent of a tristate line.
The choice of the name reg turned out to be a mistake, because the existence of registers is instead inferred based on how assignments are performed. Due to this, use of reg is essentially deprecated in favor of logic, which is actually the same type.
logic is a 1-bit, 4-state data type
bit is a 1-bit, 2-state data type which may simulate faster than logic
If a logic is also declared as a wire, it has the additional capability of supporting multiple drivers. Note that by default wire is equivalent to wire logic.
In general, the "nets" (such as wire and tri) are most suitable for designing communication buses.
Practically speaking, for RTL it usually doesn't matter whether you declare with reg, or logic, or wire. However, if you have to make an explicit declaration of a 4-state type (as opposed to when you don't), you should typically choose logic since that is what is intended by the language.
Related articles:
What’s the deal with those wire’s and reg’s in Verilog
An analysis of the "logic" data type by Cliff Cummings - 20021209
As I'm unable to add a comment I've to write what looks like a new answer but isn't. Sigh!
#e19293001, #Morgan, logic defines a 4-state variable unlike bit, and hence a logic variable can be used to store 1'bz so the following code is valid and compiles:
logic t_data;
assign t_data = (drive) ? y : 1'bz ;
But I agree that to reflect the design intent tri should be used instead of logic in these cases (although I must say I don't see people using tri instead of logic/wire too often).
reg and logic are exactly the same. These data types appear inside the always or initial blocks and store values i.e. always #(a) b <= a;, the reg b gets evaluated only when 'a' changes but otherwise it simply stores the value it has been assigned last.
wire are just simply connections and need to continuously driven. I agree that they can behave identical as #Morgan mentioned, but they can be imagined as a piece of hard wire, the value of which changes only the value at the other end or the source changes.
How do the three of them differ?
There is no difference between logic and reg.
The difference between bit and the other two is that bit is 2-state, whereas logic/reg are 4-state.
Refer to IEEE Std 1800-2017, section 6.11.2, 2-state (two-value) and 4-state (four-value) data types:
logic and reg denote the same type.
Also, section 6.3.1 Logic values:
The SystemVerilog value set consists of the following four basic values:
0 —represents a logic zero or a false condition
1 —represents a logic one or a true condition
x —represents an unknown logic value
z —represents a high-impedance state
Several SystemVerilog data types are 4-state types, which can store
all four logic values. All bits of 4-state vectors can be
independently set to one of the four basic values. Some SystemVerilog
data types are 2-state, and only store 0 or 1 values in each bit of a
vector.
Logic data type doesn't permit multiple driver. The last assignment wins in case of multiple assignment .Reg/Wire data type give X if multiple driver try to drive them with different value. Logic data type simply assign the last assignment value.
The "Reg" data type is used in procedural assignment, whereas the "Logic" data type can be used anywhere.