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

Add more vpi features #588

Closed
veripoolbot opened this issue Dec 15, 2012 · 9 comments
Closed

Add more vpi features #588

veripoolbot opened this issue Dec 15, 2012 · 9 comments
Labels
resolution: fixed Closed; fixed type: feature-IEEE Request to add new feature, described in IEEE 1800

Comments

@veripoolbot
Copy link
Contributor


Author Name: Rich Porter
Original Redmine Issue: 588 from https://www.veripool.org
Original Date: 2012-12-15
Original Assignee: Rich Porter


Hi Wilson,

As mentioned I've added a few more features to the vpi.

Can you review the deltas here please? It's not all there yet but I'd like some feedback on the style and where the right place is to store the product info and argv. Also how to retrieve the PACKAGE_STRING to identify the product, should it get baked into the verilated code? Or does e.g. verilated.cpp need to be preprocessed to drop the values in?

rporter/verilator@d77196f

or you can pull the vpi-features branch from github.com/rporter/verilator

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2012-12-15T13:42:13Z


Great work! Here's my feedback:

--- a/include/verilated.cpp
+VL_THREAD struct Verilated::VerilatedCommandArgs Verilated::s_args = {0, NULL};
 
This doesn't need to be per-thread as there's only one command line.

+++ b/include/verilated.h
@@ -236,6 +236,11 @@ private:
+    static struct VerilatedCommandArgs {
+	int          argc;
+        const char** argv;
+    } s_args;

Because this is under Verilated:: you should drop the Verilated prefix.

I was also thinking it needs to be save-restored (serialized) but the
assumption is that the restore is allowed to pass different arguments so
that's ok.  Perhaps add a comment mentioning that.

@@ -275,6 +280,7 @@ public:
+    static struct VerilatedCommandArgs* getCommandArgs() {return &s_args;}

use:

static VerilatedCommandArgs* getCommandArgs() {return &s_args;}

+++ b/include/verilated_vpi.h
@@ -41,7 +39,7 @@
 
 // Not supported yet
 #define _VL_VPI_UNIMP() \
-    vl_fatal(__FILE__,__LINE__,"",Verilated::catName("Unsupported VPI function: ",VL_FUNC))
+    VerilatedVpiError::vpi_unsupported(); vl_fatal(__FILE__,__LINE__,"",Verilated::catName("Unsupported VPI function: ",VL_FUNC));
 
Might as well clean up further and move the vl_fatal into vpi_unsupported
and have vpi_unsupported take the file and line.

@@ -250,13 +248,21 @@ struct VerilatedVpiTimedCbsCmp {
 class VerilatedVpi {
      enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 };	// Maxium callback reason
      typedef set<VerilatedVpioCb*> VpioCbSet;
      typedef set<pair<QData,VerilatedVpioCb*>,VerilatedVpiTimedCbsCmp > VpioTimedCbs;
 
+    struct product_info {
+	PLI_BYTE8* product;
+        PLI_BYTE8* version;
+    };

I know VPI isn't "const correct" but prefer verilator to be where possible
so please add a "const" here which unfortunately you'll need to cast-away
when reading in vpi_get_vlog_info.

      VpioCbSet		m_cbObjSets[CB_ENUM_MAX_VALUE];	// Callbacks for each supported reason
      VpioTimedCbs	m_timedCbs;	// Time based callbacks
+    VerilatedVpiError  *m_error_infop;
 
Nit, put the * next to the class:

VerilatedVpiError*  m_error_infop;

@@ -338,0 +344,0 @@ public:

+    static struct product_info* get_product_info() {
+        // how to import version info here?
+        // static const char* _package_string = PACKAGE_STRING;
+	static struct product_info _product_info = {(PLI_BYTE8*)"Verilator", (PLI_BYTE8*)"3.844 devel"};
+        return &_product_info;
+    };

I'd get rid of this and make a Verilated::productName() and
Verilated::productVersion() which VPI just calls.  Of couse that just
pushes the problem over ;)

In verilated.cpp roughly

const char* Verilated::productVersion() { return VERILATOR_VERSION; }

#include "verilated_config.h" at top of verilated.h

Add a verilat_config.h.in which is similar to src/config_build.h.in:

    // standard header....

    #define VERILATOR_VERSION	@PACKAGE_STRING@

Edit configure.ac:

    AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h)

Be sure to check that the generated code is sane.  Add a "clean" rule to
remove include/verilated_config.h.  Add a .gitignore rule for
include/verilated_config.h.

@@ -338,8 +344,107 @@ public:

+struct VerilatedVpiError {
+    //// Container for vpi error info
+    t_vpi_error_info m_error_info;
+    bool             m_flag;
+    char             m_buff[1024];

Use a #define VERILATOR_VPI_LINE_SIZE 8192

(1024 too likely to be hit regurally and makes the arbitrary size more obvious)

+    VerilatedVpiError() : m_flag(false) {
+	m_error_info.product = VerilatedVpi::get_product_info()->product;
+    }

+    void pli(PLI_INT32 level, string file, PLI_INT32 line, string message, ...) {
+        va_list args;
+        m_error_info.state = vpiPLI;
+        va_start(args, message);
+        vsnprintf(m_buff, sizeof(m_buff), message.c_str(), args);
+        va_end(args);
+        set(level, (PLI_BYTE8*)m_buff, (PLI_BYTE8*)file.c_str(), line);
+    }
+    void pli(PLI_INT32 level, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line, string message, ...) {
+        va_list args;
+        m_error_info.state = vpiPLI;
+        va_start(args, message);
+        vsnprintf(m_buff, sizeof(m_buff), message.c_str(), args);
+        va_end(args);
+        set(level, (PLI_BYTE8*)message.c_str(), code, file, line);
+    }
+    void set(PLI_INT32 level, PLI_BYTE8 *message, PLI_BYTE8 *file, PLI_INT32 line) {
+	m_flag=true;
+        m_error_info.level = level;
+        m_error_info.message = message;
+        m_error_info.file = file;
+        m_error_info.line = line;
+        m_error_info.code = NULL;
+        do_callbacks();
+    }
+    void set(PLI_INT32 level, PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) {
+	set(level, message, file, line);
+        m_error_info.code = code;
+        do_callbacks();
+    }


'set' is a usually a type.  I'd suggest pliError(...) and setError(...) or similar.
I think setError() should be private.

@@ -571,20 +678,24 @@ PLI_BYTE8 *vpi_get_str(PLI_INT32 property, vpiHandle object) {
 
 // delay processing
 
+void vpi_put_delays(vpiHandle object, p_vpi_delay delay_p) {
+     VerilatedVpi::error_info()->pli(vpiError, __FILE__, __LINE__, " %s is currently unimplemented", VL_FUNC);
+    _VL_VPI_UNIMP(); return;
+}
 
Here and elsewhere I don't think you need to call
VerilatedVpi::error_info()->pli... as you put this into VL_VPI_UNIMP.


@@ -624,3 +735,4 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
 		return;
 	    }
 	    default: {
+                 VerilatedVpi::error_info()->pli(vpiError, __FILE__, __LINE__, "Unsupported format (%s) for %s in %s", VerilatedVpiError::str_from_vpiVal(value_p->format), vop->fullname(), VL_FUNC);

This is ugly to read and I suspect we may change it later so, I suggest making:

#define _VL_VPI_ERROR VerilatedVpi::error_info()->pliError
#define _VL_VPI_WARN  VerilatedVpi::error_info()->pliWarning
#define _VL_VPI_ERROR_RESET VerilatedVpi::error_info()->reset

Then this becomes

_VL_VPI_ERROR(__FILE__, __LINE__, "Unsupported format (%s) for %s in %s", VerilatedVpiError::str_from_vpiVal(value_p->format), vop->fullname(), VL_FUNC);

Ideally the first parameters could be implied but this is a bit of work as
we can't use defines with arbitrary numbers of parameters as that isn't
portable.


+	} else if (value_p->format == vpiStringVal) {
+	    static VL_THREAD char out[1+VL_MULS_MAX_WORDS*4];
+	    value_p->value.str = out;
+	    switch (vop->varp()->vltype()) {
+	    case VLVT_UINT8:
+	    case VLVT_WDATA:
+            {
+		int bytes = VL_BYTES_I(vop->varp()->range().bits());
+		CData* datap = ((CData*)(vop->varDatap()));
+                int i;
+		for (i=0; i<bytes; i++) {
+                    char val = datap[bytes-i-1];
+     	            out[i] = val?val:' ';
+		}
+                out[i]=0; // NULL terminate
+		return;
+	    }

Need to check for "out" buffer overrun. as VL_MULS_MAX_WORDS only applies
to multiplies not everything.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Rich Porter
Original Date: 2012-12-17T14:35:21Z


Ok, I think I've updated all of that.

rporter/verilator@46921e1

One remaining issue is the use of vl_fatal in vpi_unsupported. Whilst I can see the value of terminating execution immediately when there has been a vpi programming issue I don't believe you want to do it all the time plus the vpi spec doesn't say to do that anyway. It is long winded to have to write a vpiPLIError callback to reimplement this behaviour so I propose something like

     void VerilatedVpiError::do_callbacks() {
         if (getError()->level < vpiError) return; // Don't do anything if not an error
         VerilatedVpi::callCbs(cbPLIError);
#ifdef VL_VPI_FATAL_ON_UNSUPPORTED
         vpi_unsupported();
#endif
     }


of course, it doesn't have to be a cpp condition, it could be on by default etc. Let me know how you'd like it done.

Anyway I need to finish deprecating _VL_VPI_UNIMP and would like to add vpiBinStrVal & friends before submitting patch. I'll need to extend the t_vpi_val testcase further and perhaps add a new one to check the unsupported stuff says it's unsupported? This will also test cbPLIError and vpi_chk_error. Need to test vpi_get_vlog_info somewhere too.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2012-12-17T15:02:47Z


Either way we might as well do it at runtime rather than compile time as it also allows someone to turn on/off the assertion for just one call if they so desire. Please add a new 'class Verilated' member variable and method for this.

Should abort on unsupported be the default or not? I suspect most people are lazy programmers and don't check for errors so lean towards forcing people to set the flag in their code and so know what they're signing up for.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Rich Porter
Original Date: 2013-01-16T13:32:45Z


The abort defaults to on - to keep the old behaviour.

I've added a new testcase t_vpi_unimpl for the unimplemented errors and extended t_vpi_var to test the put/get string values. The test is a bit contrived but it did find a few issues I've subsequently fixed.

Two things - firstly I notice that the existing put/get code doesn't mask off any upper bits that may be unused by the underlying verilog object, so it is possible to store more bits than actually exist (via vpi put/get). The vpi*Str code does ignore these bits apart from vpiDecStr which doesn't. Does this difference matter? It's trivial to add if we think it's a good idea. Secondly vpiDecStr is only supported to a maximum of 64 bits because it uses sprintf/sscanf and "%u", I think this is upper limit is a realistic bound to how it will be used - I personally would use hex (or binary) format for > 64 bits.

Also there are multiple "out" buffers for each of the different vpi_get_value formats - shall we coalesce them into one?

You can pull these changes thus ```git pull https://github.com/rporter/verilator.git vpi-features

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2013-01-17T02:36:05Z


The code should mask off bits in "put" that are not implemented, you'll get very odd results if those bits ever are non-zero as Verilator optimizes some of them as always being zero. "get" can assume they are zero in the interior model.

Yes %d is 64 bits out of laziness (er, efficiency). No one has noticed yet, but you're welcome to fix it.

I probably would merge "out", helps the dcache.

I'll wait for an update before merging unless you think it'll be a while.

Some little notes all minor, in general looks great.

--- a/include/verilated_vpi.h

+#define _VL_VPI_ERROR_SET_ \
+        va_list args; \
+        va_start(args, message); \
+        vsnprintf(m_buff, sizeof(m_buff), message.c_str(), args); \
+        va_end(args);

Please wrap this in a 'do { ... } while (0)' and drop trailing _ so it can
be called with a trailing semicolon so that emacs indents don't get upset.

+class VerilatedVpiError {
+...
+    VerilatedVpiError() : m_flag(false) {

When you make a constructor always make a destructor even if empty.  (See "Exceptional C++")

+    static const char* str_from_vpiVal(PLI_INT32 vpiVal) {
+	static const char *names[] = {
+	    "*undefined*",
+	    "vpiBinStrVal",

First the mix of underscores and studlyCaps kills me :) I'm inconsistent
but usually avoid underscores in Verilator-internal method names.

I think these tables or the whole function definitions should be in
verilated_vpi.cpp as there's no reason to burden every header include with
compiling the function.

Also I presume you made them from the spec, but it seems a bit error prone;
if this stuff changed a lot I would script it, but it's so static it isn't
worth it.  Perhaps a test for at least the last value in each table would
be good, as otherwise one deletion in the beginning screws everything.

+++ b/test_regress/t/t_vpi_var.cpp
+#define DEBUG if (0) printf

Don't ever define DEBUG unless it's for assert.h's usage, call it TEST_MSG or something.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Rich Porter
Original Date: 2013-01-17T15:33:22Z


I've added masking logic to all the vpi_put_value types including the original vpiVectorVal and vpiIntVal which didn't have it either.

I've left vpiDecStr at 64 bits max, trying to use a bigger number results in value of -1 and sets all the bits. I would be interested to know what the commercial simulators do, can they use decimal representations > 64 bits? I might have tried harder if there was a standard library that supported arbitrary length integers.

Merged "out" into a single buffer, sized for vpiBinStrVal which is the biggest (in char size) representation.

Addressed each of the review comments - hopefully acceptable fixes. There were 'issues' with the string enumerations too - added some test coverage in t_vpi_var.

Committed & pushed to github.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2013-01-18T02:41:56Z


Great! Only a few trivial space & compiler warning cleanups.

Pushed to git towards 3.845.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2013-02-05T03:21:52Z


In 3.845.

@veripoolbot
Copy link
Contributor Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2013-02-08T23:10:55Z


Rich, please look at #�, thanks. (Can't forward bugs in this web tool.)

@veripoolbot veripoolbot added resolution: fixed Closed; fixed type: feature-IEEE Request to add new feature, described in IEEE 1800 labels Dec 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
resolution: fixed Closed; fixed type: feature-IEEE Request to add new feature, described in IEEE 1800
Projects
None yet
Development

No branches or pull requests

1 participant