Using SystemVerilog mailbox type as module IO - system-verilog

Is it possible to pass around a mailbox types in SystemVerilog using normal module IO? Most of the book examples of mailbox only show them being used inside of SystemVerilog classes. For example, could I do the follow? Is it legal?
module driver(
output mailbox mb
);
mailbox mbx = new(2);
initial begin
for (int i=0; i < 5; i++) begin
#1 mbx.put(i);
$display ("[%0t] Put #%0d, size=%0d",
$time, i, mbx.num());
end
end
endmodule
module sink(
input mailbox mb
);
initial begin
forever begin
int idx;
#2 mbx.get (idx);
$display ("[%0t] Got item #%0d, size=%0d",
$time, idx, mbx.num());
end
end
endmodule
module top;
mailbox mbx;
source(mbx);
sink(mbx);
endmodule

Yes, but your code has many errors. You were mixing up mb and mbx, and your module top was incorrect. Also, I strongly recommend using a parameterized mailbox to make is strongly typed. You'll thank me later when using many mailboxes. The following works for me
typedef mailbox#(int) i_mailbox;
module source(
output i_mailbox mbx
);
initial begin
mbx = new(2);
for (int i=0; i < 5; i++) begin
#1 mbx.put(i);
$display ("[%0t] Put #%0d, size=%0d",
$time, i, mbx.num());
end
#100 $finish;
end
endmodule
module sink(
input i_mailbox mbx
);
initial begin
forever begin
int idx;
#2 mbx.get (idx);
$display ("[%0t] Got item #%0d, size=%0d",
$time, idx, mbx.num());
end
end
endmodule
module top;
i_mailbox mbx;
source so(mbx);
sink si(mbx);
endmodule

Related

How to pass interface modport signal into other module?

I'm trying to implement an interface with modports. I have 2 modules inside a testbench.
module test;
test_intf intf();
clk_reset u_clk_reset (
intf.CLK_RESET
);
mem u_mem(
intf.MEM
);
....
endmodule
This is the interface:
interface test_intf;
logic reset_n;
logic aclk;
logic bclk;
logic areset_n;
logic wr_in;
logic rd_en;
logic [ 9:0] wr_addr;
logic [23:0] wr_data;
logic [ 9:0] rd_addr;
logic [23:0] rd_data;
modport CLK_RESET (output aclk, bclk, areset_n);
modport MEM (output rd_data, input clk, reset_n, wr_in, rd_en, wr_addr, wr_data, rd_addr);
endinterface
This is the clk_reset module:
module clk_reset(
test_intf intf
);
always #(5) intf.aclk = ~intf.aclk;
initial begin
intf.aclk = 0;
end
initial begin
intf.areset_n = 1;
#2000 intf.areset_n = 0;
#2000 intf.areset_n = 1;
end
This is the mem module:
module mem
(
test_intf intf
);
always #(posedge intf.clk or negedge intf.reset_n)
begin
//end
end
I checked that the clk_reset interface ports are working (all outputs).
But, the problem is that the output clock signals from clk_reset can't reach the mem module by interface. So, intf.clk signal is 'x' in the mem module.
How do I pass some interface's signals into another interface (module)?
Your simulator should have given you a compile error. The interface is missing the declaration of the clk signal. Add it like this:
logic clk;
Since you want to use clk inside mem, you need to add clk to the CLK_RESET modport list:
modport CLK_RESET (output aclk, bclk, clk, areset_n);
Finally, you need to generate the clk signal so that it is not X. Here are the required changes:
interface test_intf;
logic reset_n;
logic aclk;
logic bclk;
logic clk;
logic areset_n;
logic wr_in;
logic rd_en;
logic [ 9:0] wr_addr;
logic [23:0] wr_data;
logic [ 9:0] rd_addr;
logic [23:0] rd_data;
modport CLK_RESET (output aclk, bclk, clk, areset_n);
modport MEM (output rd_data, input clk, reset_n, wr_in, rd_en, wr_addr, wr_data, rd_addr);
endinterface
module clk_reset(
test_intf intf
);
always #(5) intf.aclk = ~intf.aclk;
initial begin
intf.aclk = 0;
end
always #(5) intf.clk = ~intf.clk;
initial begin
intf.clk = 0;
end
initial begin
intf.areset_n = 1;
#2000 intf.areset_n = 0;
#2000 intf.areset_n = 1;
end
endmodule

How do I design Serial to Parallel Buffer in Verilog only using clocks?

I am looking to design a serial to parallel converter in Verilog which converts a fast clock serial input to a slower clock parallel input. I tried the following code which works in RTL but does not verify on Cadence Conformal. nclk is 16 times faster than clk. The serial data comes in at nclk and the parallel data is intended to come out at clk rate.
sEEG - Serial Input
eegOut - Parallel output
I can only have clk and nclk as my operation references due to tape-out bond pad limitations.
Following is the code that I have come up with which works well in functional simulation but Formal Verification fails.
module deserializer(sEEG, nclk, clk, eegOut);
input sEEG;
input nclk,clk;
reg [15:0] temp;
output reg [15:0] eegOut;
reg [4:0] i;
always #(negedge nclk) begin
temp[i] = sEEG;
i = i + 1;
end
always#(posedge clk) begin
i<=0;
eegOut <= temp;
end
endmodule
I feel you should use four bits in order to index 16 elements. If you parameterize the module, this could be done with:
# (parameter WIDTH = 16)
then later use it as:
localparam BIT_SEL = $clog2(WIDTH); //..this should give you "4" if WIDTH = 16
reg [BIT_SEL-1:0] i;
Also, you might want to include a clock synchronizer, you don't want metastability problems, an easy way to do this is to include "double-triggers", practically is to buffer the data and replicate it to the next slow clock cycle (adds 1 slow clock cycle latency). So, maybe this works:
module deserializer
# (parameter WIDTH = 16)
(sEEG, nclk, clk, eegOut);
input sEEG;
input nclk,clk;
reg [WIDTH-1:0] temp;
reg [WIDTH-1:0] temp_reg; //..synchronizer
output reg [WIDTH-1:0] eegOut;
always #(negedge nclk) begin
temp[WIDTH-2:0] <= temp[WIDTH-1:1];
temp[WIDTH-1] <= sEEG;
end
always#(posedge clk) begin
temp_reg <= temp;
eegOut <= temp_reg;
end
endmodule

How to bind an interface with system verilog module?

I have an system verilog interface
interface add_sub_if(
input bit clk,
input [7:0] a,
input [7:0] b,
input doAdd,
input [8:0] result
);
clocking dut_cb #(posedge clk);
output a;
output b;
output doAdd;
input result;
endclocking // cb
modport dut(clocking dut_cb);
endinterface: add_sub_if
And i have a SV module which uses this interface
module dummy(add_sub_if.dut _if);
....
endmodule: dummy
What is the ideal way to hook up this in my TB?
If i instantiate the interface, i need to create wires.
If i use bind then also i need to do a port mapping of the individual signals, that beats the convenience of having an interface.
Another add on question is, how to assign one such interface to another interface?
Thanks in advance,
Rajdeep
You can find a simple example of an interface definition and usage in the IEEE Std 1800-2012 Section 3.5 (Interfaces). It shows how to define the interface and hook it up to the design (the way you have already done it). It also shows how the interface can be instantiated (and connected) inside a top level module/wrapper (I copied the code below directly from the spec for your convenience):
interface simple_bus(input logic clk); // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
endinterface: simple_bus
module memMod(simple_bus a); // simple_bus interface port
logic avail;
// When memMod is instantiated in module top, a.req is the req
// signal in the sb_intf instance of the 'simple_bus' interface
always #(posedge clk) a.gnt <= a.req & avail;
endmodule
module cpuMod(simple_bus b); // simple_bus interface port
...
endmodule
module top;
logic clk = 0;
simple_bus sb_intf(.clk(clk)); // Instantiate the interface
memMod mem(.a(sb_intf)); // Connect interface to module instance
cpuMod cpu(.b(sb_intf)); // Connect interface to module instance
endmodule
Once you have the interface hooked up, then you can have drive/sample all the signals from a testcase program (just remember that you have to pass the interface to it). In this case, it would be be something like:
program testcase(simple_bus tb_if);
initial begin
tb_if.mode <= 0;
repeat(3) #20 tb_if.req <= 1'b1;
[...]
$finish;
end
endprogram
For a real-world example, you can check the source code of a UVM testbench that is available on my GitHub page. The interface hook-up is done in the xge_test_top.sv file.
Here is fsm dut with testbench.
This fsm dut performs state transition 1 - 0 - 1 - 0 in sequence.
Test bench verifies dut is properly working or not.
verilog module code(dut) :
module melay_fsm(o,clk,rst,i);
output o;
input i,clk,rst;
reg o;
reg [1:0]state;
// [1:0]state;
always#(posedge clk,posedge rst)
begin
if(rst)
begin
state <=2'b00;
end
else
begin
case(state)
2'b00:
begin
if(i)
state<=2'b01;
else
state<=2'b00;
end
2'b01:
begin
if(!i)
state<=2'b10;
else
state<=2'b01;
end
2'b10:
begin
if(i)
state<=2'b11;
else
state<=2'b00;
end
2'b11:
begin
if(!i)
state<=2'b00;
else
state<=2'b01;
end
endcase
end
end
always#(posedge clk,negedge rst)
begin
if(rst)
o<=1'b0;
else if(state==2'b11 && i==0)
o<=1'b1;
else
o<=1'b0;
end
endmodule
System verilog module code(testbench) :
interface melay_intf(input bit clk);
logic o,rst,i;
wire clk;
clocking c1#(posedge clk);
input o;
output i,rst;
endclocking
modport tes(clocking c1);
endinterface
module top;
bit clk;
always
#1 clk = ~clk;
melay_intf i1(clk);
melay_fsm a1(.o(i1.o),.clk(i1.clk),.rst(i1.rst),.i(i1.i));
melay_tes(i1);
endmodule
program melay_tes(melay_intf i1);
initial
#100 $finish;
initial
begin
i1.rst <= 0;
#4 i1.rst <= 1;
#4 i1.rst <= 0;
i1.i = 1;
#2 i1.i = 0;
#2 i1.i = 1;
#2 i1.i = 0;
#2 i1.i = 1;
#2 i1.i = 0;
repeat(10)
begin
i1.i = 1;
#2 i1.i = $urandom_range(0,1);
end
end
initial
$monitor("output = %d clk = %d rst = %d i = %d",i1.o,i1.clk,i1.rst,i1.i);
initial
begin
$dumpfile("mem.vcd");
$dumpvars();
end
endprogram
Important thing to note down here is connection of signals in top module.
melay_fsm a1(.o(i1.o),.clk(i1.clk),.rst(i1.rst),.i(i1.i));
Please, observe properly how I bind interface with testbench and dut.
Please, observe following things.
I defines interface with all the dut's signals.
I took instance(i1) of interface(melay_intf) in top module.
I took instance (a1) of dut (melay_fsm) in top module.
Now observes melay_fsm a1(.o(i1.o),.clk(i1.clk),.rst(i1.rst),.i(i1.i))
All the dut's signals are connected with interface.
I passed instance of interface(i1) in testbench. melay_tes(i1)
So, testbench can access interface signals and interface signals are connected to dut's signals.
Now, you can access dut's signals in your test bench with help of interface.
I think now you can understand proper flow.
Please ask the question if you have any doubt.
You can bind interface in system verilog module.
Here, I provides sample code with help of that you can understood how to bind interface in system verilog module and with dut.
Here I provides verilog module and system verilog module. Main part of code is interface from which verilog and system verilog module are connected.
verilog module code(dut) :
module dff(qn,d,clk,reset);
output qn;
input d,clk,reset;
reg qn;
always#(posedge clk,negedge reset)
begin
if (!reset)
begin
qn=1'bx;
end
else if (d==0)
begin
qn=0;
end
else if (d==1)
begin
qn=1;
end
end
endmodule
System verilog module code(testbench) :
interface melay_intf(input bit clk);
logic o,clk,rst,i;
clocking c1#(posedge clk);
input o;
output i,rst;
endclocking
endinterface
module top;
bit clk;
always
#1 clk = ~clk;
melay_intf i1(clk);
dff d1(.o(i1.o),.clk(i1.clk),.rst(i1.rst),.i(i1.i));
melay_tes(i1.tes);
endmodule
program melay_tes(melay_intf i1);
initial
#100 $finish;
initial
begin
i1.rst <= 0;
#4 i1.rst <= 1;
#4 i1.rst <= 0;
i1.i = 1;
#2 i1.i = 0;
#2 i1.i = 1;
#2 i1.i = 0;
#2 i1.i = 1;
#2 i1.i = 0;
repeat(10)
begin
i1.i = 1;
#2 i1.i = $urandom_range(0,1);
end
end
initial
$monitor("output = %d clk = %d rst = %d i = %d",i1.o,i1.clk,i1.rst,i1.i);
initial
begin
$dumpfile("mem.vcd");
$dumpvars();
end
endprogram
Here important part is interface and in it I used clocking block for synchronization purpose. Here clocking c1#(posedge clk); so all signals which are mention inside the clocking block which are i,o,rst.All this signal change its value at every posedge of clk signal.
Here dff d1(.o(i1.o),.clk(i1.clk),.rst(i1.rst),.i(i1.i)); Important thing that you find in top module I made connection between verilog signals and system verilog signals.
You can find verilog module name is "dff". I took the instance of dff verilog module and made the connection. Here i1.o,i1.clk,i1.rst,i1.i is system verilog signals which are connected to o,clk,rst,i signals of verilog module of with dot convention.

can packed arrays be passed by reference to the task in systemverilog

Can s_clk be passed as argument to xyz task in below code?
module test(input logic m_clk, output [1:0] logic s_clk);
...
xyz (m_clk,s_clk);//assuming m_clks and s_clks are generated from top
...
task automatic xyz (ref logic clk1, ref [1:0] logic clk2);
...
endtask
endmodule
I have read your problem, first of all you have typo mistake
module test(input logic m_clk, output [1:0] logic s_clk);
task automatic xyz (ref logic clk1, ref [1:0] logic clk2);
instead of this you have to write
module test(input logic m_clk, output logic [1:0] s_clk);
task automatic xyz (ref logic clk1, ref logic [1:0] clk2);
For better understanding I have also share one demo code for packed arrays can be passed by reference to the task in systemverilog.
Here is code :
program main();
bit [31:0] a = 25;
initial
begin
#10 a = 7;
#10 a = 20;
#10 a = 3;
#10 $finish;
end
task pass_by_val(int i);
$monitor("===============================================%d",i);
forever
#a $display("pass_by_val: I is %0d",i);
endtask
task pass_by_ref(ref bit [31:0]i);
forever
begin
#a $display("pass_by_ref: I is %0d",i[0]);
$display("This is pass_by value a ====== %d \n a[0] ====== %0d ",a,a[0]);
end
endtask
initial
begin
pass_by_val(a);
end
initial
pass_by_ref(a);
endprogram
By running this example you can observe that packed arrays can be passed by reference to the task in systemverilog and its value is also reflected to it.
pass_by_val task will register the value of the variables
only once at the time when task is called. Subsequently when the variable changes its value, pass_by_val task cannot see the newer values. On the other hand, 'ref' variables in a task are registered whenever its value changes. As a result, when the variable 'a' value changes, the pass_by_ref task can register and display the value correctly.
I simulated Ashutosh Rawal's code and the output display is given below:
=============================================== 25
pass_by_val: I is 25
pass_by_ref: I is 1
This is pass_by value a ====== 7
a[0] ====== 1
pass_by_val: I is 25
pass_by_ref: I is 0
This is pass_by value a ====== 20
a[0] ====== 0
pass_by_val: I is 25
pass_by_ref: I is 1
This is pass_by value a ====== 3
a[0] ====== 1
$finish called from file "testbench.sv", line 13.
$finish at simulation time 40
V C S S i m u l a t i o n R e p o r t

Parallel To Serial HDL

I am making a parallel to serial converter using ring counter in verilog. The ring counter is working fine but the Parallel to serial converter is not working properly and I am getting x undefined result. I am providing the code kindly help me finding the problem.
TOP
module PtoSTOP;
reg clk,rst;
wire [3:0] myout;
wire out;
Ring a(clk,rst,myout);
parToser x(myout,clk,rst,out);
initial begin
clk=1;
rst=1;
#1 rst=0;
end
always
#2 clk=~clk;
endmodule
Parallel TO Serial Converter
module parToser(myout,clk,rst,out);
input clk,rst;
input [3:0] myout;
output reg out;
reg [2:0]i;
always #(posedge clk or posedge rst) begin
if(rst) begin
out <= 0;
i <= 0;
end
else begin
out <= myout[i];
i <= i+1;
end
end
endmodule
RingCounter
module Ring(clk,rst,myout);
input clk,rst;
output reg [3:0]myout;
always #(posedge clk or posedge rst) begin
if(rst)
myout<=1;
else
myout<=myout<<1;
end
endmodule
I think the main issue you are seeing is part of parToser.
You have reg [2:0]i; which you increment and use to address input [3:0] myout; but i can hold values 0 to 7, half of which is outside the address range of [3:0] myout. You should be seeing a simulation error about out of range addressing.
Also you have included a few flip-flops with a reset condition but not added the reset to the sensitivity list in 'parToser' & 'Ring':
always #(posedge clk)
Should be:
always #(posedge clk or posedge rst)
With out this trigger your out, i and myout variables will be x, as they have not been set to a known condition.
NB: parToser i = i+1; should be i <= i+1;