Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using --public in verilator #2071

Closed
BracketMaster opened this issue Dec 28, 2019 · 26 comments
Closed

using --public in verilator #2071

BracketMaster opened this issue Dec 28, 2019 · 26 comments
Labels
type: q and a Question and answer about some feature or user question

Comments

@BracketMaster
Copy link

BracketMaster commented Dec 28, 2019

I and currently trying to use verilator as the backend simulator for the Python Migen HDL. I know this was done with Scala Chisel. In order to be useful, I need to peek at hierarchical signals. Unit tests often do look and inputs and output in submodules. I read that --public might result in mis-simulations for clocks. What exactly does this mean? How do you suggest I go about exposing sub netlist signals?

I should add that I don't plan to modify sub signals - only read them.

@BracketMaster BracketMaster added the new New issue not seen by maintainers label Dec 28, 2019
@BracketMaster
Copy link
Author

Actually - I found that verilator will sometime generate a VL_SIG for internal module signals and other time not. What determines this behavior? This could be useful...

@wallento
Copy link
Member

Similar work was recently done for cocotb (https://github.com/cocotb/cocotb). It uses VPI and currently the preferred way is to use --public-flat-rw. Also be aware of a patch I will soon land that allows you to select the exposed variables via a configuration file (#1514). ETA for the PoC is the next couple of hours, I am polishing the first iteration right now.

@wallento
Copy link
Member

And you should not mess with anything not explicitly exposed as public.

The preferred way is to use DPI, but VPI is probably what you are looking at.

@wallento
Copy link
Member

See #2072

@BracketMaster
Copy link
Author

OK. This seems reasonable. I also saw something about Python support in #1363 and in the README.adoc - but it doesn't seem to work yet. I posted a new issue on this(#2074 ) and am closing this issue.

@BracketMaster
Copy link
Author

@wallento actually - I just took a look at cocotb. It seems like there is a branch of cocotb that supports VPI?

@BracketMaster BracketMaster reopened this Dec 29, 2019
@BracketMaster
Copy link
Author

BracketMaster commented Dec 29, 2019

And you should not mess with anything not explicitly exposed as public.

In my Vtop.h I see:

VL_MODULE(Vtop) {
  public:
    
    // PORTS
    // The application code writes and reads these signals to
    // propagate new values into/out from the Verilated model.
    // Begin mtask footprint all: 
    VL_IN8(op,0,0);
    VL_IN16(a,15,0);
    VL_IN16(b,15,0);
    VL_OUT16(o,15,0);
    
    // LOCAL SIGNALS
    // Internals; generally not touched by application code
    // Begin mtask footprint all: 
    VL_SIG8(top__DOT__op,0,0);
    VL_SIG16(top__DOT__a,15,0);
    VL_SIG16(top__DOT__add_a,15,0);
    VL_SIG16(top__DOT__add_b,15,0);
    VL_SIG16(top__DOT__add_o,15,0);

I assume its ok to access the local VL_SIGs?
I only wish to read their value and never to modify them.

@wallento
Copy link
Member

cocotb is based on VPI, it is the mainline. You can find the recent PR where I added support for Verilator here: cocotb/cocotb#943
It is a good starting point for your work I believe, what are you planning by the way?

It is safe to read them, but I propose to use --public-flat-rw instead, it gives you access to the signals in a more reliable way.

@wallento wallento added type: q and a Question and answer about some feature or user question and removed new New issue not seen by maintainers labels Dec 30, 2019
@BracketMaster
Copy link
Author

I'm adding a verilator backend to nMigen(then new and improved Migen).
https://github.com/m-labs/nmigen

@BracketMaster
Copy link
Author

Although I said I would only be reading internals - it turns out there is a useful cases for writing internals too. But even though the rw in --public-rw would imply that internals can be written, trying to write to them in verilator does nothing...

@wsnyder
Copy link
Member

wsnyder commented Jan 3, 2020

Most likely you write a value then the next eval overwrites it, it is not a force. Make sure you are e.g. writing a flop's storage that is not getting loaded (or not clocked).

@BracketMaster
Copy link
Author

I had a top module that has an add submodule. add.a is connected to top.a, and add.o is connected to top.o, but add.b is unconnected.

doing top->top__DOT__add_b = 5 and then top->eval() does not seem to do anything...

@wallento
Copy link
Member

wallento commented Jan 3, 2020

Like this?

test.v:

module t (input [7:0] a, output [7:0] o);
   adder add (.a(a), .b(), .o(o));
endmodule

module adder (input [7:0] a, input [7:0] b, output [7:0] o);
   assign o = a + b;
endmodule

test.cpp:

#include "Vtest.h"
#include <cstdio>

int main(int argc, char** argv) {
    Vtest top;
    top.a = 1;

    top.eval(); printf("%d\n", top.o);
    top.t__DOT__add__DOT__b = 1; top.eval(); printf("%d\n", top.o);
    top.t__DOT__add__DOT__b = 255; top.eval(); printf("%d\n", top.o);
}

Build:

verilator --cc --exe test.v test.cpp --public-flat-rw
make -C obj_dir/ -f Vtest.mk

Run:

$ ./obj_dir/Vtest 
1
2
0

Can you elaborate a bit more if you want to do random accesses? Can you synthesize DPI into the design? Inserting DPI into your modules is the most portable and fast way.

@BracketMaster
Copy link
Author

Huh. That's strange^^.. Lemme see what I was missing.
I'd have to get the yosys emitter to annotate with DPI. Do you have a quick example of the code you just pasted with DPI?

@wallento
Copy link
Member

wallento commented Jan 3, 2020

Ah, its from yosys, than DPI may probably only be an option if you put a wrapper around your design:

test_wrap.v

module t_wrap (input [7:0] a, output [7:0] o);
   t dut (.a(a), .o(o));

   export "DPI-C" function write_b;
   function void write_b(byte data);
       dut.add.b = data;
   endfunction
endmodule

Updated test.cpp:

#include "Vtest_wrap.h"
#include <cstdio>

extern void write_b(char);

int main(int argc, char** argv) {
    Vtest_wrap top;
    top.a = 1;

    svSetScope(svGetScopeFromName("TOP.t_wrap"));
    top.eval(); printf("%d\n", top.o);
    top.write_b(1); top.eval(); printf("%d\n", top.o);
    top.write_b(255); top.eval(); printf("%d\n", top.o);
}

Build and run:

$ verilator --cc --exe test_wrap.v test.v test.cpp
$ make -C obj_dir/ -f Vtest_wrap.mk
$ ./obj_dir/Vtest_wrap 
1
2
0

Generating accessors in the modules by yosys is both a rabbit hole and probably out of scope for yosys.

@BracketMaster
Copy link
Author

DPI is definitely the way to go then!
Any idea the cost in speed though?
I know VPI was pretty slow...

@BracketMaster
Copy link
Author

Also, do you know how I can guarantee DPI is thread safe?
I see that verilator has the options:

--threads-dpi all
--threads-dpi none
--threads-dpi pure

On designs of notable size, it should make sense to enable --threads,
However, Python only calls into the cpp wrapper from a single thread - which is fine...
Do I even have to worry about thread safety with DPI if I peek/poke my multithreaded verilated model from only a single thread?

@wallento
Copy link
Member

wallento commented Jan 3, 2020

Well, the trade-off between VPI and DPI is that VPI can access arbitrary signals (hence --public-flat-rw) and DPI is limited to compile time accesses (dynamicity is only the scope). For that you get a speed of DPI comparable to directly accessing the signals. The latter is the fastest, but limited to Verilator. I would say the speed difference between DPI and Verilator native is negligible..

@wallento
Copy link
Member

wallento commented Jan 3, 2020

About threads @wsnyder can probably comment better

@BracketMaster
Copy link
Author

Thanks again. Lastly, I've gotten reads and writes to work for signals less than 32 bits long with DPI. But how can I return a 75 bit signal for example? I imagine verilator should see this as some sort of array of chars or perhaps uints...

@wsnyder
Copy link
Member

wsnyder commented Jan 3, 2020

I would suggest not using --threads-dpi, which will use the default pure option. If you benchmark and find DPI uses most of the time, possibly the all option could be used but the app's DPI C code likely needs fixing to support that.

@BracketMaster
Copy link
Author

OK - I figured out how to get DPI accesses of arbitrary width.

@BracketMaster
Copy link
Author

BracketMaster commented Jan 4, 2020

I've pasted example getter and setter DPI methods for working with widths greater than 64 should any one else find these useful.

rm -rf obj_dir/
verilator --cc --exe test_wrap.v test.v test.cpp
make -C obj_dir/ -f Vtest_wrap.mk
./obj_dir/Vtest_wrap 

test.v

module t (input [71:0] a, output [71:0] o);
   adder add (.a(a), .b(), .o(o));
endmodule

module adder (input [71:0] a, input [71:0] b, output [71:0] o);
   assign o = a + b;
endmodule

test_wrap.v

module t_wrap (input [71:0] a, output [71:0] o);
   t dut (.a(a), .o(o));

   export "DPI-C" function read_a;
   function void read_a(output bit [71:0] result);
       result = dut.add.a;
   endfunction

   export "DPI-C" function write_a;
   function void write_a(input bit [71:0] in);
       dut.add.a = in;
   endfunction

endmodule

test.cpp

#include "Vtest_wrap.h"
#include <cstdio>

extern void write_b(char);

void print_by_int(uint32_t* val){
	printf("print_by_int\n");
	for(int i = 0; i < 3; i++)
		{
		printf("%d: ",i);
		printf("%2x\n",val[i]);
		}
}

int main(int argc, char** argv) {
    Vtest_wrap top;

    svSetScope(svGetScopeFromName("TOP.t_wrap"));

	//set and read top.a using verilator public method
	top.a[0] = 1;
	top.a[1] = 2;
	top.a[2] = 3;
	top.eval();
	printf("from verilator ");
	print_by_int(top.a);

	//set top.a using DPI and read with DPI
	uint32_t ret[] = {0,0,128};
	top.write_a((svBitVecVal *)&ret);
	printf("from DPI ");
	print_by_int(ret);

	//read top.a with verilator public method - should
	//match output of DPI
	printf("from verilator ");
	print_by_int(top.a);

	top.final();
	return 0;
}

output

from verilator print_by_int
0:  1
1:  2
2:  3
from DPI print_by_int
0:  0
1:  0
2: 80
from verilator print_by_int
0:  0
1:  0
2: 80

@BracketMaster
Copy link
Author

So DPI exporting worked with smaller designs. But now I am trying to simulate a RISCV CPU and verilator is changing the names of the DPI functions I defined in its header file.

For example, I originally had read_rom_bus__adr which is now declared in Vtop.has read_rom_bus___05Fadr.

The header file also has this new section called internal methods with declarations such as this

    static void __Vdpiexp_top_wrap__DOT__read_rom_bus___05Fack_TOP(Vtop__Syms* __restrict vlSymsp, CData& out);

I should note that I wrote DPI access methods for all the port signals in my verilog generated from nMigen. So this is about a few hundred... Could this simply be too many DPI methods for verilator to handle?

I'm also quite certain that the names have no special symbols.

@BracketMaster
Copy link
Author

Turns out I did have some illegal characters. All good now.

@wallento
Copy link
Member

wallento commented Jan 6, 2020

Great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: q and a Question and answer about some feature or user question
Projects
None yet
Development

No branches or pull requests

3 participants