I am trying to create a uvm type test bench for a phase accumulator which is to be used in a DDS chain. Having this may be an overkill for a simple phase accumulator, but I am new to uvm/SystemVerilog so want to learn how to use the concepts. I have several questions.
My DUT:
module phase_acc #(
parameter PHASE_ACC_WIDTH = 16,
parameter TUNNING_WORD_WIDTH = 8
) (
input wire clk,
input wire reset,
input wire load,
input wire [TUNNING_WORD_WIDTH-1 : 0] tuning_word,
output reg [PHASE_ACC_WIDTH-1 : 0] phase,
output wire cycle_end,
output wire [1:0] phase_quadrant
);
assign phase_quadrant = phase[PHASE_ACC_WIDTH-1:PHASE_ACC_WIDTH-2];
assign cycle_end = (phase + tuning_word) > {PHASE_ACC_WIDTH{1'b1}};
always #(clk) begin
if (reset == 1) begin
phase <= 0;
end else begin
phase <= phase + tuning_word;
end
end
endmodule
Please note that this is not complete and planning to fill it after writing tests.
basic functionality is that the phase_acc counts up by tuning_word at a time. Just before it wraps around, it needs to assert cycle_end. Forget about phase_quadrant for now.
A new tuning_word can be loaded by asserting load.
I have created two classes to monitor signals. One for input stimulus (tuning_word and load) which gets triggered when ever load is one during a clock transition. The other is for phase_acc outputs which contain phase, cycle_end. Since these change at each clock transition, a new object of this type need to be created at each clock edge. Monitor uses two analysis ports to send stimulus packets and output packets to scoreboard.
My first question is, does this design make sense? Most uvm examples I saw have a behavior where after a stimulus is created, the DUT gives a single output which is then verified. (Say a RAM for example). In my case however, the DUT is free running.
If above design is correct, I have my second question.
My code for the monitor is:
class monitor extends uvm_monitor;
`uvm_component_utils(monitor)
function new(string name = "monitor", uvm_component parent = null);
super.new(name, parent);
endfunction
uvm_analysis_port #(acc_stimulus) mon_stimulus_analysis_port;
uvm_analysis_port #(acc_output) mon_output_analysis_port;
virtual reg_if vif;
semaphore sema4;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual acc_if)::get(this, "", "acc_if", vif))
`uvm_fatal("MON", "Could not get vif")
sema4 = new(1);
mon_stimulus_analysis_port = new("mon_stimulus_analysis_port", this);
mon_output_analysis_port = new("mon_output_analysis_port", this);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
fork
begin
#(vif.clk);
// output
acc_output out = new;
out.phase = vif.phase;
out.cycle_end = vif.cycle_end;
out.phase_quadrant = cif.phase_quadrant;
`uvm_info(get_type_name(), $sformatf("Monitor found ouout %s", out.convert2str()),
UVM_LOW)
mon_output_analysis_port.write(out);
end
begin
#(vif.clk);
// stimulus
if (vif.ld) begin
acc_stimulus stimulus = new;
stimulus.tunning_word = vif.tuning_word;
stimulus.load = vif.ld;
`uvm_info(get_type_name(), $sformatf("Monitor found stimulus %s",
stimulus.convert2str()), UVM_LOW)
mon_stimulus_analysis_port.write(stimulus);
end
end
join
end
endtask
endclass
But when trying to compile this, I get an error for the line which says:
acc_output out = new;
I am not sure what I am doing wrong there. If I were to do this inside an if condition, for example:
begin
#(vif.clk);
if(vif.clk) begin
// output
acc_output out = new;
Then the error disappears.
I cannot understand what's wrong with the code.
You get the error because you declared the out variable in the middle of a begin/end block, after a statement:
begin
#(vif.clk); // This is a statement
// output
acc_output out = new; // This is a variable declaration
Inside a begin/end block, you must declare all variables before statements.
The error went away when you declared the variable in the if clause because it was the first line of code after the begin keyword. The fact that this was inside an if condition is irrelevant. The key was that you created a new block of code with the begin keyword.
This should also work:
begin
// output
acc_output out = new;
#(vif.clk);
Related
module processor_testbench (
input [7:0] o_cmd_status_reg,
input o_start_ack,
output reg [7:0] i_transmit_data, // transmit data port
input o_transmit_data_request, // transmit data required
output reg i_rst_n,
output reg i_clk
);
initial begin
i=1'b0;
end
initial begin //-> Where the problem is
#(posedge(o_start_ack));
i=i+1;
if(i==1)
i_transmit_data = 8'bxxx00000;
else if(i==2)
i_transmit_data=8'b00000000;
else if(i==3)
i_transmit_data=8'b11110000;
else if(i==4)
i_transmit_data=8'b00000000;
//stop trasmission #start acknowledge
end
endmodule
I want to see the result (simulation) when the o_start_ack become rising edge.
i_transmit_data is changing
8'bxxx00000 -> 8'b00000000 -> 8'b11110000 -> 8'b00000000
But, the simulation shows only 8'bxxx00000.
Codes that are not used in this case are removed by me.
How can I make i_transmit_data have various value?
Change your second initial block to an always block:
always begin
#(posedge(o_start_ack));
i=i+1;
// ...
The problem with your code is that the initial block only waits for one posedge of the o_start_ack signal. Once it sees the 1st posedge, it sets i=1 and i_transmit_data=8'bxxx00000, then the block stops executing.
Using an always block allows the code to execute every time there is a posedge of o_start_ack.
I have some variables in an initial block
initial
begin
i = 32'b0;
j = 32'b1;
end
I want to initialize them with initial values like this every time I press a pushbutton
always #(posedge btn)
begin
i = 32'b0;
j = 32'b1;
end
Doing it like this gives the error "can't resolve multiple constant driver" and I know why it happens but, is there another way around??
It sounds like you are creating synthesizable code (based on your need to press a button). Initial blocks do not synthesize to logic, they are only used for simulation. Typically you use a reset signal to set initial values.
Also you generally want to keep the logic for any one signal in a single block, instead of separating it into separate blocks. (again, this is for synthesizeable code, for simulation this is not important)
Finally, You generally do not want to use outside async signals to clock some logic (unless you know what you are doing). You would instead code something like:
//---- detect rising edge of btn ----
reg btn_prev;
wire rising_edge_btn;
always #(posedge clk)
btn_prev <= btn;
assign rising_edge_btn = ~btn_prev & btn;
// ---- i and j logic --------------
always #(posedge clk) begin
if( rst || rising_edge_btn) begin
i <= 0;
j <= 1;
end
else
//some other logic here
end
end
the code above uses a synchronous reset signal "rst". You can also find designs with an asynchronous reset. It would be good practice to also synchronize your outside async btn signal with with 2 flip flops to avoid metastability.
When I compile my code I get these error msg for following lines. can someone explain it.
This is verilog code for a processor
assign Imm = instruction[7:0];
assign OUT1addr = instruction[2:0];
assign OUT2addr = instruction[10:8];
assign INaddr = instruction[18:16];
assign address = instruction[23:16];
assign address = instruction[7:0];
The following message comes for ABOVE LINES
tgt-vvp sorry: procedural continuous assignments are not yet fully supp
orted. The RHS of this assignment will only be evaluated once, at the time the assignment statement is executed.
You did not do what I asked which is show me where that code is.
From the error message I very much suspect that code is inside an always block:
always #( ...)
...
assign Imm = instruction[7:0];
This is called "a procedural continuous assignment".
The alternative is:
always #( ...)
...
Imm = instruction[7:0];
This is a standard assignment.
There is a significant difference between the two. You would normally not use the first form (unless you really, really know what you are doing.)
Thus the solution is to remove all the 'assign' keywords if they are inside an always block.
Outside an always you need the assign:
always #( * )
begin
...
x = y ^ z;
end
assign write = valid & select;
The short answer is you should probably remove the assign keyword.
The assign keyword has two different meanings depending on context you do not show.
When used at the top level of a module, the assign keyword is a permanent process sensitive to RHS changes and assigns it to the LHS wire. The assign statement has equivalent functionality to the always block below
module mod;
...
assign Awire = B + C;
always #(B or C) begin
Areg = B + C;
end
endmodule
When used inside a procedural process, it is a temporary process that assigns the LHS variable every time the RHS changes. The two always blocks below have the same functionality
module top;
...
always #(sel)
begin
if (sel)
assign Areg = B;
else
assign Areg = C;
end
always #(sel or B or C) // #*
begin
if (sel)
Areg = B;
else
Areg = C;
end
endmodule
Unfortunately, almost all synthesis tools require you to write your code with a complete sensitivity list as in the latter always block. Thus this eliminates allowing the use of assign inside a procedural block.
From testbench, I have to corrupt a bus in design.
I am using a random variable to select a bit location (bit_sel)
bit_sel = $urandom_range(0,MAX_LENGTH-1);
Bus is somewhere deep inside the RTL with a width of MAXLENGTH.
wire [MAX_LENGTH-1:0] BUS_TO_BE_FORCED;
In TB, I am using following line to corrupt the bus:
force TOP.DUT.....BUS_TO_BE_FORCED[bit_sel] = ~TOP.DUT.....BUS_TO_BE_FORCED[bit_sel];
But, I am getting compilation error. What is the best way to do this? I want to flip only one bit.
You could instead flip the bits using a XOR mask:
wire [MAX_LENGTH-1:0] corrupt_bits = 1 << $urandom_range(0,MAX_LENGTH-1);
force BUS_TO_BE_FORCED = corrupt_bits ^ BUS_TO_BE_FORCED;
The LHS must be a constant bit-select of a vector net (among other things). So,
force TOP.DUT.....BUS_TO_BE_FORCED[0]
is Ok, but
force TOP.DUT.....BUS_TO_BE_FORCED[bit_sel]
isn't. You could try a big case statement, because the selectors don't have to be constant:
case(bit_sel)
0: force TOP.DUT.....BUS_TO_BE_FORCED[0] = ...
...etc
EDIT: the initial answer contained my guesses, along with completely wrong information. I should not have answered this question in the first place, therefore I had to write test-benches and confirm the following statements to compensate for the mess.
1) Bit select must be a constant (at compile time). Even the following code (perfectly reasonable in my opinion) won't pass elaboration:
integer bit_sel;
initial begin
bit_sel = 0;
force BUS_TO_BE_FORCED[bit_sel] = 1'b1;
end
2) If used inside "initial" block, the following statement is fine:
force BUS_TO_BE_FORCED[SOME_PARAM] = ~BUS_TO_BE_FORCED[some_index];
SOME_PARAM is a parameter
some_index may be a variable or a net
SOME_PARAM = some_index
However, the same statement inside "always" block causes the simulation to hang. This issue may be resolved by adding delay:
#1 force BUS_TO_BE_FORCED[SOME_PARAM] = ~BUS_TO_BE_FORCED[some_index];
3) The answer by Eric is a very elegant way around language's limitations, but it is also subject to limitations described in section 2 above - you'll have to add delay if you want to use it in "always" block.
I had a similar problem and resorted to using another vector of equal width to the signal to be corrupted. I also encapsulated this in an interface so that I could bind it to any part of the DUT as necessary. Refer to the code below:
import uvm_pkg::*;
`include "uvm_macros.svh"
interface sync_signal_fault_injector #(
parameter int SIGNAL_WIDTH = 1
) (
input clk,
input reset,
input [SIGNAL_WIDTH-1:0] signals
);
bit [SIGNAL_WIDTH-1:0] toggle_bits = '0;
class sync_signal_fault_injector_c extends uvm_object implements fivip_pkg::Injectable;
function new(string name="fault_injector");
super.new(name);
endfunction
virtual function int unsigned get_size();
return SIGNAL_WIDTH;
endfunction
virtual task inject(ref int unsigned indices[], input int unsigned delay);
repeat (delay) #(posedge clk);
foreach (indices[id]) begin
int unsigned bit_index = indices[id];
if (bit_index >= get_size()) begin
`uvm_fatal("BOUNDS",
$sformatf("Tried to access bit %0d but signal bus is only of size %0d", id, get_size())
)
end
// Prepare toggle bits
toggle_bits[bit_index] = 1;
end
force signals = signals ^ toggle_bits;
#(posedge clk);
release signals;
// Reset toggle bits
toggle_bits = '0;
endtask
endclass
sync_signal_fault_injector_c fault_injector = new;
endinterface
I have a school project in Verilog and I am very newbie at it. A part of the program is this
integer x;
assign x=1;
**LINE 49** while(x<=9)
begin
assign lastBitsofP=P[1:0];
if(lastBitsofP == 2'b00 || lastBitsofP ==2'b11)
begin
rightShift r1(shiftedValue,P);
end
x=x+1;
end
but I always get this error : "mainModule.v" line 49 expecting 'endmodule', found 'while' ,
You need to stop coding and think about what is going on. You are modelling hardware and connections. When you write assign x = that means "I have a wire and I want you to drive that wire with this value". If you have a module like r1 that you want connected it must be connected always you can't go "oh wait, if this happens just create a multiply unit for me".
You need to instantiate your connections at the start. If you only want the right shifted value sometimes then you can have a statement like assign out = select ? shiftedValue : unshiftedValue; And then you just need to write the logic for select.
And you'll probably want a flip-flop for your output. Something like
reg [31:0] result;
always #(posedge clk)
begin
result <= out;
end