To introduce co-simulation, we will continue to use the Gray encoder example from the previous chapters. Suppose that we want to synthesize it and write it in Verilog for that purpose. Clearly we would like to reuse our unit test verification environment.
To start, let's recall how the Gray encoder in MyHDL looks like:
def bin2gray(B, G, width):
""" Gray encoder.
B -- input intbv signal, binary encoded
G -- output intbv signal, gray encoded
width -- bit width
"""
@always_comb
def logic():
for i in range(width):
G.next[i] = B[i+1] ^ B[i]
return logic
To show the co-simulation flow, we don't need the Verilog implementation yet, but only the interface. Our Gray encoder in Verilog would have the following interface:
module bin2gray(B, G); parameter width = 8; input [width-1:0] B; output [width-1:0] G; ....
To write a test bench, one creates a new module that instantiates the design under test (DUT). The test bench declares nets and regs (or signals in VHDL) that are attached to the DUT, and to stimulus generators and response checkers. In an all-HDL flow, the generators and checkers are written in the HDL itself, but we will want to write them in MyHDL. To make the connection, we need to declare which regs & nets are driven and read by the MyHDL simulator. For our example, this is done as follows:
module dut_bin2gray;
reg [`width-1:0] B;
wire [`width-1:0] G;
initial begin
$from_myhdl(B);
$to_myhdl(G);
end
bin2gray dut (.B(B), .G(G));
defparam dut.width = `width;
endmodule
The $from_myhdl task call declares which regs are driven by
MyHDL, and the $to_myhdl task call which regs & nets are read
by it. These tasks take an arbitrary number of arguments. They are
defined in a PLI module written in C and made available in a
simulator-dependent manner. In Icarus Verilog, the tasks are defined
in a myhdl.vpi module that is compiled from C source code.
About this document