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

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.

Related

recomend the way to write a monitor in UVM with defferent event polarity

I am trying to implement a monitor for VDU(Video display unit) and the way the VDU can be programmed says that sync signals have controllable polarity. This means than according to VDU settings monitor should react on #posedge or #negedge event. Is there any way to pass the type (means posesge or negedge) via configuration data base or do something like this. Instead of write if(truth) #posedge else #negedge. And assertion also needs to be controlled this way but assertion at list designed to take event type as an argument but I am no sure config data base calls are allowed inside interface.
On option is to conditionally trigger an event. For example, you can have the bellow in you interface:
event mon_clk_ev;
bit mon_polarity;
always #(posedge clk) if ( mon_polarity) ->mon_clk_ev;
always #(negedge clk) if (!mon_polarity) ->mon_clk_ev;
Then you can use mon_clk_ev are the clock event in your monitor, interface, clocking block, or assertion.
mon_polarity could be assigned by your monitor, uvm_config_db, or other logic.
Example using uvm_config_db (Note using uvm_bitstream_t so it can be assigned with the uvm_set_config_int plusarg):
initial begin
start_of_simulation_ph.wait_for_state( UVM_PHASE_STARTED, UVM_GTE );
if (!uvm_config_db#(uvm_bitstream_t)::exists(null,"","mon_polarity")) begin
// default if not in database
uvm_config_db#(uvm_bitstream_t)::set(null,"*","mon_polarity",1'b1);
end
forever begin
void'(uvm_config_db#(uvm_bitstream_t)::get(null,"","mon_polarity",mon_polarity));
uvm_config_db#(uvm_bitstream_t)::wait_modified(null,"","mon_polarity");
end
end
You should write your code assuming positive polarity, but feed them through an xor operator.
logic signal; // your signal from DUT
logic signal_corrected; // signal with positive polarity
bit signal_polarity; // 0 = positive ; 1 = negative
assign signal_corrected = signal ^ signal_polarity;
Now you can use signal_corrected in your assertions. You can certainly call uvm_config_db#(bit)::get() from the interface if it has been set in your testbench. You might need to use uvm_config_db#(bit)::wait_modified() to wait for it to be set before you get it.

Possible workaround for async negedge reset?

I'd like to have a register with async reset signal, like following:
always #(posedge clk or negedge rst_n)
begin
if(!rst_n)
out <= 1'b0
else
out <= in
end
I have tried class AsyncReset() and withReset(). However, the generated code uses a posedge reset and the variable of AsyncReset() does not accept !.
Is there any workaround for this?
While you cannot invert the AsyncReset type directly (generally applying logic to an AsyncReset is bad because it can glitch), you can cast to a Bool and back:
val reset_n = (!reset.asBool).asAsyncReset
val reg = withReset(reset_n)(RegInit(0.U(8.W)))
Runnable example: https://scastie.scala-lang.org/ERy0qHt2Q3OvWIsp9qiiNg
I thought Jack's quick comment about avoiding glitches deserved a longer explanation.
Using an asynchronous reset creates a second timing arc in the design, from the reset to the end flop. The reset signal can be asserted at any time but needs to be de-asserted synchronous to the clock otherwise the flop can become metastable.
A common technique to do this is to use a reset synchronizer.
https://scastie.scala-lang.org/hutch31/EPozcu39QBOmaB5So6fyeA/13
The synchronizer shown in the above code is coded directly in Verilog as I do not know a way to keep the FIRRTL optimizer from pruning through constant optimization. The logic downstream of the reset sync can be either sync or async reset.

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

Mixing nonblocking and blocking assignments in an always_ff block of an arbiter

I'm unable to wrap my head around Example 10-3 in SystemVerilog For Design book by Stuart Sutherland (and co.).
See line 232 of :
https://code.google.com/p/vak-opensource/source/browse/trunk/hardware/systemverilog/utopia-example/squat.sv?r=185
Here is the snippet of code. My question will follow.
bit [0:NumRx-1] RoundRobin;
always_ff #(posedge clk, posedge reset) begin: FSM
bit breakVar;
if (reset) begin: reset_logic
Rxready <= '1;
Txvalid <= '0;
Txsel_out <= '0;
SquatState <= wait_rx_valid;
forward <= 0;
RoundRobin = 1;
end: reset_logic
else begin: FSM_sequencer
unique case (SquatState)
wait_rx_valid: begin: rx_valid_state
Rxready <= '1;
breakVar = 1;
for (int j=0; j<NumRx; j+=1) begin: loop1
for (int i=0; i<NumRx; i+=1) begin: loop2
if (Rxvalid[i] && RoundRobin[i] && breakVar)
begin: match
ATMcell <= RxATMcell[i];
Rxready[i] <= 0;
SquatState <= wait_rx_not_valid;
breakVar = 0;
end: match
end: loop2
if (breakVar)
RoundRobin={RoundRobin[1:$bits(RoundRobin)-1],
RoundRobin[0]};
end: loop1
end: rx_valid_state
Specifically, my question is about the blocking assignment for breakVar and RoundRobin. I read somewhere that the variables are locally evaluated, but I can't picture in terms of gates how the logic is synthesized. Does RoundRobin get synthesized to a state register?
Most guidelines state to never mix blocking and nonblocking assignments. Is there a better way to represent something like this? Is it okay now in SystemVerilog designs to mix both types of assignments given that it is in an always_ff block?
You should never mix blocking and nonblocking assignments to the same variable. breakVar is a temporary variable that will be synthesized into combinatorial logic because it is always written to first, then read. There is no state to be saved. RoundRobin is a local variable that is being used as both an intermediate and state variable. But because it is only accessed from within the always_ff block, there is no danger of a race condition.
A temporary variable is just a symbolic way to represent a piece of an equation. Here is a different but simpler example:
always_ff #(posedge clock)
begin
full = (counter == 10);
brimming = (counter > 7);
hold <= brimming && !full;
if (full)
counter <= counter + 1;
else
counter < = 0;
end
This is equivalent to writing the following (but might be harder to understand)
always_ff #(posedge clock)
begin
hold <= (counter > 7) && !(counter == 10);
if (counter == 10)
counter <= counter + 1;
else
counter < = 0;
end
In the two examples above, counter will always be synthesized as a register because it is read before written. It won't matter if we used a blocking or nonblocking assignment because we never read counter after writing it. There is no race condition within this always_ff block using blocking assignment but there could be if there was another always_ff block trying to read it. Since full and brimming are written before being read, they do not have to be registered.
To summarize, a variable get synthesized as a register if any of these conditions are true
A variable is read before being written within the same always block. Note that even if a non-blocking assignment statement appears first, the read happens first because the write get scheduled to happen later.
Due to conditional or looping statements, a variable is sometimes read without being written
A variable is written in an always_ff block and read outside the block.
Totally agreed with #jonathan answer.
You should always split your logic elements in always_comb block and sequential elements in always_ff block.
If you write a code that is so closely stitched together ( both combi
and sequential elements in same block) even though it is correct and
compliant with system verilog spec, some older versions of simulator
or newer simulators being developed may infer it in wrong way.
Your code will not be clean and comprehensible to others.
Also by writing in above style you are just compacting the lines of
code, even though the logic remains same. There is no sense in
writing a compact code if it hampers the readability of the code.
Now as far as blocking and non-blocking statements usage is concerned, I think that debate is closed now. It is now more a rule than a guideline to use blocking statements in always_comb block and non-blocking in always_ff block.
However the answer to all your questions are explained in this superb paper by Clifford E Cummings
Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!
And if you are new to verilog/system verilog design I suggest you read all their papers, They are very useful and sets up a good base for a RTL Design Engineer.
Also it may be too much to tell here but if you are looking how to segregate your code in combi and sequential block you can have a look at code generated by bluespec
The signal names are difficult to comprehend in one go, but if you look closely the code is very neat logically and does not leave anything on the whims of simulation and synthesis tools.