Support user-defined primitives (UDPs) (#468) (#5807) (#5936)

Co-authored-by: Bartłomiej Chmiel <bchmiel@antmicro.com>
Co-authored-by: Zhou Shen <599239118@qq.com>
This commit is contained in:
Krzysztof Sychla 2025-04-16 12:32:18 +02:00 committed by GitHub
parent 6a6a7b7e9f
commit d0c4cc3938
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 814 additions and 191 deletions

View File

@ -178,6 +178,7 @@ set(HEADERS
V3Trace.h
V3TraceDecl.h
V3Tristate.h
V3Udp.h
V3Undriven.h
V3UniqueNames.h
V3Unknown.h
@ -326,6 +327,7 @@ set(COMMON_SOURCES
V3TraceDecl.cpp
V3Tristate.cpp
V3TSP.cpp
V3Udp.cpp
V3Undriven.cpp
V3Unknown.cpp
V3Unroll.cpp

View File

@ -311,6 +311,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Trace.o \
V3TraceDecl.o \
V3Tristate.o \
V3Udp.o \
V3Undriven.o \
V3Unknown.o \
V3Unroll.o \

View File

@ -1798,19 +1798,48 @@ class AstUdpTable final : public AstNode {
public:
AstUdpTable(FileLine* fl, AstUdpTableLine* linesp)
: ASTGEN_SUPER_UdpTable(fl) {
addLinesp(linesp);
this->addLinesp(linesp);
if (!v3Global.hasTable()) v3Global.setHasTable();
}
ASTGEN_MEMBERS_AstUdpTable;
};
class AstUdpTableLine final : public AstNode {
string m_text;
// @astgen op1 := iFieldsp : List[AstUdpTableLineVal] // Input fields
// @astgen op2 := oFieldsp : List[AstUdpTableLineVal] // Output fields
private:
const bool m_udpIsCombo; // Combinational or sequential UDP
public:
AstUdpTableLine(FileLine* fl, const string& text)
class UdpCombo {};
AstUdpTableLine(UdpCombo, FileLine* fl, AstUdpTableLineVal* iFieldsp,
AstUdpTableLineVal* oFieldsp)
: ASTGEN_SUPER_UdpTableLine(fl)
, m_text{text} {}
, m_udpIsCombo{true} {
addIFieldsp(iFieldsp);
addOFieldsp(oFieldsp);
}
class UdpSequential {};
AstUdpTableLine(UdpSequential, FileLine* fl, AstUdpTableLineVal* iFieldsp,
AstUdpTableLineVal* oFieldsp1, AstUdpTableLineVal* oFieldsp2)
: ASTGEN_SUPER_UdpTableLine(fl)
, m_udpIsCombo{false} {
addIFieldsp(iFieldsp);
addOFieldsp(oFieldsp1);
addOFieldsp(oFieldsp2);
}
ASTGEN_MEMBERS_AstUdpTableLine;
int udpIsCombo() const { return m_udpIsCombo; }
};
class AstUdpTableLineVal final : public AstNode {
string m_text; // Value character
public:
AstUdpTableLineVal(FileLine* fl, const string& text)
: ASTGEN_SUPER_UdpTableLineVal(fl)
, m_text{text} {}
ASTGEN_MEMBERS_AstUdpTableLineVal;
string name() const override VL_MT_STABLE { return m_text; }
void name(std::string const& text) override VL_MT_STABLE { m_text = text; }
string text() const VL_MT_SAFE { return m_text; }
};
class AstVar final : public AstNode {

View File

@ -118,6 +118,7 @@ class V3Global final {
bool m_hasEvents = false; // Design uses SystemVerilog named events
bool m_hasClasses = false; // Design uses SystemVerilog classes
bool m_hasSampled = false; // Design uses SAMPLED expresions
bool m_hasTable = false; // Desgin has the UDP Table.
bool m_hasVirtIfaces = false; // Design uses virtual interfaces
bool m_usesProbDist = false; // Uses $dist_*
bool m_usesStdPackage = false; // Design uses the std package
@ -182,6 +183,8 @@ public:
void setHasClasses() { m_hasClasses = true; }
bool hasSampled() const { return m_hasSampled; }
void setHasSampled() { m_hasSampled = true; }
bool hasTable() const { return m_hasTable; }
void setHasTable() { m_hasTable = true; }
bool hasVirtIfaces() const { return m_hasVirtIfaces; }
void setHasVirtIfaces() { m_hasVirtIfaces = true; }
bool usesProbDist() const { return m_usesProbDist; }

View File

@ -109,14 +109,6 @@ class InstVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
void visit(AstUdpTable* nodep) override {
if (!v3Global.opt.bboxUnsup()) {
// If we support primitives, update V3Undriven to remove special case
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Verilog 1995 UDP Tables. "
"Use --bbox-unsup to ignore tables.");
}
}
// Save some time
void visit(AstNodeExpr*) override {}
void visit(AstNodeAssign*) override {}

237
src/V3Udp.cpp Normal file
View File

@ -0,0 +1,237 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Implementation of User defined primitives
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Udp's Transformations:
//
// For every table line create an always block containing if statements
// with condition depending on a combination of the input fields:
//
// 0 1 0 on a, b, c turns into !a&b&~c
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Udp.h"
#include "V3Error.h"
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
class UdpVisitor final : public VNVisitor {
bool m_inInitial = false; // Is inside of an initial block
AstVar* m_oFieldVarp = nullptr; // Output filed var of table line
std::vector<AstVar*> m_inputVars; // All the input vars in the AstPrimitive
std::vector<AstVar*> m_outputVars; // All the output vars in the AstPrimitive
AstPrimitive* m_primp = nullptr; // The current primitive
bool m_isFirstOutput = false; // Whether the first IO port is output
AstVarRef* m_outputInitVerfp = nullptr; // Initial output value for sequential UDP
AstAlways* m_alwaysBlockp = nullptr; // Main Always block in UDP transform
void visit(AstInitial* nodep) override {
VL_RESTORER(m_inInitial);
if (m_primp) m_inInitial = true;
iterateChildren(nodep);
}
void visit(AstAssign* nodep) override {
if (m_inInitial) { m_outputInitVerfp = VN_CAST(nodep->lhsp(), VarRef); }
iterateChildren(nodep);
}
void visit(AstPrimitive* nodep) override {
UASSERT_OBJ(!m_primp, nodep, "Primitives cannot nest");
VL_RESTORER(m_primp);
VL_RESTORER(m_outputInitVerfp);
VL_RESTORER(m_isFirstOutput);
VL_RESTORER(m_inputVars);
VL_RESTORER(m_outputVars);
m_outputInitVerfp = nullptr;
m_primp = nodep;
m_isFirstOutput = false;
iterateChildren(nodep);
m_inputVars.clear();
m_outputVars.clear();
}
void visit(AstVar* nodep) override {
// Push the input and output vars for primitive.
if (m_primp) {
if (nodep->isIO()) {
if (nodep->isInput()) {
m_inputVars.push_back(nodep);
} else {
m_outputVars.push_back(nodep);
}
if ((m_inputVars.size() == 0) && (m_outputVars.size() == 1)) {
m_isFirstOutput = true;
}
}
}
iterateChildren(nodep);
}
void visit(AstUdpTable* nodep) override {
FileLine* const fl = nodep->fileline();
if (m_outputVars.size() != 1) {
m_outputVars.back()->v3error(
m_outputVars.size()
<< " output ports for UDP table, there must be one output port");
}
if (!m_isFirstOutput && m_outputVars.size()) {
m_inputVars[0]->v3error("First UDP port must be the output port");
}
m_oFieldVarp = m_outputVars[0];
m_alwaysBlockp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, nullptr};
fl->warnOff(V3ErrorCode::LATCH, true);
iterateChildren(nodep);
nodep->replaceWith(m_alwaysBlockp);
}
void visit(AstUdpTableLine* nodep) override {
FileLine* const fl = nodep->fileline();
if (!nodep->udpIsCombo() && !m_oFieldVarp->isBitLogic()) {
m_oFieldVarp->v3error("For sequential UDP, the output must be of 'reg' data type");
}
if (nodep->udpIsCombo() && m_oFieldVarp->isBitLogic()) {
m_oFieldVarp->v3error(
"For combinational UDP, the output must not be a 'reg' data type");
}
AstNode* iNodep = nodep->iFieldsp();
AstNode* oNodep = nodep->oFieldsp();
uint32_t inputvars = 0;
AstSenTree* edgetrigp = nullptr;
AstLogAnd* logandp = new AstLogAnd{fl, new AstConst{fl, AstConst::BitTrue{}},
new AstConst{fl, AstConst::BitTrue{}}};
for (AstVar* itr : m_inputVars) {
if (!iNodep) break;
inputvars++;
if (AstUdpTableLineVal* linevalp = VN_CAST(iNodep, UdpTableLineVal)) {
string valName = linevalp->name();
AstVarRef* const referencep = new AstVarRef{fl, itr, VAccess::READ};
if (isEdgeTrig(valName)) {
if (nodep->udpIsCombo()) {
linevalp->v3error(
"There should not be a edge trigger for combinational UDP table line");
}
if (edgetrigp) {
linevalp->v3error("There can be only one edge tigger signal");
}
edgetrigp = new AstSenTree{
fl, new AstSenItem{fl, VEdgeType::ET_BOTHEDGE,
new AstVarRef{fl, itr, VAccess::READ}}};
}
if (valName == "0" || valName == "f")
logandp = new AstLogAnd{fl, logandp, new AstLogNot{fl, referencep}};
else if (valName == "1" || valName == "r")
logandp = new AstLogAnd{fl, logandp, referencep};
}
iNodep = iNodep->nextp();
}
if (inputvars != m_inputVars.size()) {
nodep->v3error("Incorrect number of input values, expected " << m_inputVars.size()
<< ", got " << inputvars);
}
string const oValName = nodep->udpIsCombo() ? oNodep->name() : oNodep->nextp()->name();
if (oValName == "-") {
if (edgetrigp) pushDeletep(edgetrigp);
if (logandp) pushDeletep(logandp);
return;
}
if (!nodep->udpIsCombo()) {
AstVarRef* const referencep = new AstVarRef{fl, m_oFieldVarp, VAccess::READ};
if (oNodep->name() == "0") {
logandp = new AstLogAnd{fl, logandp, new AstLogNot{fl, referencep}};
} else if (oNodep->name() == "1") {
logandp = new AstLogAnd{fl, logandp, referencep};
}
}
fl->warnOff(V3ErrorCode::LATCH, true);
AstIf* const ifp
= new AstIf{fl, logandp,
new AstAssign{fl, new AstVarRef{fl, m_oFieldVarp, VAccess::WRITE},
new AstConst{fl, getOutputNum(nodep, oValName)}}};
if (nodep->udpIsCombo()) {
if (!isCombOutputSig(oValName)) {
oNodep->v3error("Illegal value for combinational UDP line output");
}
m_alwaysBlockp->addStmtsp(ifp);
return;
}
if (!isSequentOutputSig(oValName)) {
oNodep->nextp()->v3error("Illegal value for sequential UDP line output");
}
m_alwaysBlockp->addNext(new AstAlways{fl, VAlwaysKwd::ALWAYS, edgetrigp, ifp});
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstLogAnd* nodep) override { iterateChildren(nodep); }
void visit(AstLogNot* nodep) override { iterateChildren(nodep); }
// For logic processing.
bool isEdgeTrig(std::string& valName) {
if (valName == "*") return true;
if (valName == "01" || valName == "p" || valName == "P" || valName == "r"
|| valName == "R") {
valName = "r";
return true;
}
if (valName == "10" || valName == "n" || valName == "N" || valName == "f"
|| valName == "F") {
valName = "f";
return true;
}
if (valName.size() == 2) {
if (valName[0] == '1' || valName[1] == '0')
valName = "f";
else if (valName[0] == '0' || valName[1] == '1')
valName = "r";
return true;
}
if (valName[0] != '0' && valName[0] != '1') { valName = "?"; }
return false;
}
bool isCombOutputSig(const std::string& valName) {
return (valName == "0" || valName == "1" || valName == "x" || valName == "X");
}
bool isSequentOutputSig(const std::string& valName) {
return (valName == "0" || valName == "1" || valName == "x" || valName == "X"
|| valName == "-");
}
V3Number getOutputNum(AstNode* nodep, const std::string& fieldNames) {
V3Number outputNum{nodep, 1};
if (fieldNames == "0") {
outputNum.setBit(0, 0);
} else if (fieldNames == "1") {
outputNum.setBit(0, 1);
} else {
outputNum.setBit(0, 'x');
}
return outputNum;
}
public:
// CONSTRUCTORS
explicit UdpVisitor(AstNetlist* nodep) { iterate(nodep); }
~UdpVisitor() override = default;
};
void V3Udp::udpResolve(AstNetlist* rootp) {
UINFO(4, __FUNCTION__ << ": " << endl);
{ const UdpVisitor visitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("udp", 0, dumpTreeEitherLevel() >= 3);
}

32
src/V3Udp.h Normal file
View File

@ -0,0 +1,32 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Implementation of User defined primitives
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3UDP_H_
#define VERILATOR_V3UDP_H_
#include "config_build.h"
#include "verilatedos.h"
class AstNetlist;
//============================================================================
class V3Udp final {
public:
static void udpResolve(AstNetlist* rootp) VL_MT_DISABLED;
};
#endif // Guard

View File

@ -102,6 +102,7 @@
#include "V3Trace.h"
#include "V3TraceDecl.h"
#include "V3Tristate.h"
#include "V3Udp.h"
#include "V3Undriven.h"
#include "V3Unknown.h"
#include "V3Unroll.h"
@ -183,7 +184,9 @@ static void process() {
// Remove any modules that were parameterized and are no longer referenced.
V3Dead::deadifyModules(v3Global.rootp());
v3Global.checkTree();
if (v3Global.hasTable()) V3Udp::udpResolve(v3Global.rootp());
// Create a hierarchical Verilation plan
if (!v3Global.opt.lintOnly() && !v3Global.opt.serializeOnly()

View File

@ -1009,20 +1009,23 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
/************************************************************************/
/* Attributes */
/* Note simulators vary in support for "(* /_*something*_/ foo*)" where _ doesn't exist */
<V95,V01NC,V01C,V05,VA5,S05,S09,S12,S17,S23,SAX>{
<V95,V01NC,V01C,V05,VA5,S05,S09,S12,S17,S23,SAX,TABLE>{
"(*"({ws}|{crnl})*({id}|{escid}) { yymore(); yy_push_state(ATTRMODE); } /* Doesn't match (*), but (* attr_spec */
}
/************************************************************************/
/* Tables */
<TABLE>\\{crnl} { yymore(); }
<TABLE>{crnl} { yymore(); }
<TABLE>";" { FL; yylval.strp = PARSEP->newString(yytext, yyleng); return yaTABLELINE; }
<TABLE>"endtable" { yy_pop_state(); FL; return yENDTABLE; }
<TABLE>"`line"{ws}+[^\n\r]*{crnl} { FL_FWD; PARSEP->lexPpline(yytext); FL_BRK; }
<TABLE>. { yymore(); }
<TABLE><<EOF>> { FL; yylval.fl->v3error("EOF in 'table'");
yyleng = 0; yy_pop_state(); FL_BRK; yyterminate(); }
<TABLE>[rRfFpPnN\*] { FL; yylval.strp = PARSEP->newString(yytext, yyleng); return yaTABLE_FIELD; } /* edge_symbol */
<TABLE>[01xX\?bB\-] { FL; yylval.strp = PARSEP->newString(yytext, yyleng); return yaTABLE_FIELD; } /* level_symbol, next_state */
<TABLE>":" { FL; return yaTABLE_LRSEP; } /* LHS and RHS separator for table line. */
<TABLE>";" { FL; return yaTABLE_LINEEND; }
<TABLE>[\(\)] { FL; return yytext[0]; }
<TABLE>{ws}|(\\){0,1}{crnl} { FL_FWD; FL_BRK; }
<TABLE>"`line"{ws}+[^\n\r]*{crnl} { FL_FWD; PARSEP->lexPpline(yytext); FL_BRK; }
<TABLE>"//"[^\n]* { FL_FWD; FL_BRK; } /* throw away single line comments */
<TABLE>"endtable" { FL; yy_pop_state(); return yENDTABLE; }
<TABLE><<EOF>> { FL; yylval.fl->v3error("EOF in 'table'");
yyleng = 0; yy_pop_state(); FL_BRK; yyterminate(); }
<EDGEDESC>{
01|10|[01][zZxX]|[zZxX][01] { FL; return yaEDGEDESC; }

View File

@ -450,7 +450,9 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> ygenSTRENGTH "STRENGTH keyword (strong1/etc)"
%token<strp> yaTABLELINE "TABLE LINE"
%token<strp> yaTABLE_FIELD "UDP table field"
%token<fl> yaTABLE_LRSEP ":"
%token<fl> yaTABLE_LINEEND "UDP table line end"
%token<strp> yaSCCTOR "`systemc_ctor block"
%token<strp> yaSCDTOR "`systemc_dtor block"
@ -5785,14 +5787,26 @@ combinational_body<nodep>: // IEEE: combinational_body + sequential_body
yTABLE tableEntryList yENDTABLE { $$ = new AstUdpTable{$1, $2}; }
;
tableEntryList<udpTableLinep>: // IEEE: { combinational_entry | sequential_entry }
tableEntry { $$ = $1; }
| tableEntryList tableEntry { $$ = addNextNull($1, $2); }
tableEntryList<udpTableLinep>: // IEEE: { combinational_entry + sequential_entry }
tableLine { $$ = $1; }
| tableEntryList tableLine { $$ = addNextNull($1, $2); }
;
tableEntry<udpTableLinep>: // IEEE: combinational_entry + sequential_entry
yaTABLELINE { $$ = new AstUdpTableLine{$<fl>1, *$1}; }
| error { $$ = nullptr; }
tableLine<udpTableLinep>:
tableInputList yaTABLE_LRSEP tablelVal yaTABLE_LINEEND
{ $$ = new AstUdpTableLine{AstUdpTableLine::UdpCombo{}, $<fl>1, $1, $3}; }
| tableInputList yaTABLE_LRSEP tablelVal yaTABLE_LRSEP tablelVal yaTABLE_LINEEND
{ $$ = new AstUdpTableLine{AstUdpTableLine::UdpSequential{}, $<fl>1, $1, $3, $5}; }
;
tableInputList<udpTableLineValp>:
tablelVal { $$ = $1; }
| tableInputList tablelVal { $$ = addNextNull($1, $2); }
;
tablelVal<udpTableLineValp>:
yaTABLE_FIELD { $$ = new AstUdpTableLineVal{$<fl>1, *$1}; }
| '(' yaTABLE_FIELD yaTABLE_FIELD ')' { $$ = new AstUdpTableLineVal{$<fl>2, *$2 + *$3}; }
;
//************************************************

View File

@ -10,11 +10,9 @@
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_udp.v"
test.compile(fails=test.vlt_all, expect_filename=test.golden_filename)
test.compile()
if not test.vlt_all:
test.execute()
test.execute()
test.passes()

View File

@ -0,0 +1,68 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Mike Thyer.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
reg a, b, sel, z;
udp_mux2(z, a, b, sel);
int cycle=0;
always @(posedge clk) begin
cycle <= cycle+1;
if (cycle==0) begin
a = 0;
b = 1;
sel = 0;
end
else if (cycle==1) begin
a = 1;
b = 1;
sel = 0;
if (z != 0) $stop;
end
else if (cycle==2) begin
a = 0;
b = 1;
sel = 0;
if (z != 1) $stop;
end
else if (cycle==3) begin
a = 1;
b = 0;
sel = 0;
if (z != 0) $stop;
end
else if (cycle==4) begin
if (z != 1) $stop;
end
else if (cycle >= 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
primitive udp_mux2 (z, a, b, sel);
output z;
input a, b, sel;
table
//a b s o
? 1 1 : 1 ;
? 0 1 : 0 ;
1 ? 0 : 1 ;
0 ? 0 : 0 ;
1 1 x : 1 ;
// Next blank line is intentional for parser
// Next \ at EOL is intentional for parser
0 0 x \
: 0 ;
endtable
endprimitive

View File

@ -1,5 +0,0 @@
%Error-UNSUPPORTED: t/t_udp.v:104:4: Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables.
104 | table
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -1,149 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2009 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc = 0;
reg [63:0] crc;
reg [63:0] sum;
// Take CRC data and apply to testblock inputs
wire [31:0] in = crc[31:0];
/*AUTOWIRE*/
// Async clears must not race with clocks if we want repeatable results
reg set_l = in[20];
reg clr_l = in[21];
always @ (negedge clk) begin
set_l <= in[20];
clr_l <= in[21];
end
//====== Mux
wire [1:0] qm;
// delay z a b sel
udp_mux2 #(0.1) m0 (qm[0], in[0], in[2], in[4]);
udp_mux2 #0.1 m1 (qm[1], in[1], in[3], in[4]);
`define verilatorxx
`ifdef verilatorxx
reg [1:0] ql;
reg [1:0] qd;
// No sequential tables, yet
// always @* begin
// if (!clk) ql = in[13:12];
// end
always @(posedge clk or negedge set_l or negedge clr_l) begin
if (!set_l) qd <= ~2'b0;
else if (!clr_l) qd <= 2'b0;
else qd <= in[17:16];
end
`else
//====== Latch
// wire [1:0] ql;
// // q clk d
// udp_latch l0 (ql[0], !in[8], in[12]);
// udp_latch l1 (ql[1], !in[8], in[13]);
//====== DFF
wire [1:0] qd;
//always @* $display("UL q=%b c=%b d=%b", ql[1:0], in[8], in[13:12]);
// q clk d set_l clr_l
udp_dff d0 (qd[0], in[8], in[16], set_l, clr_l);
udp_dff d2 (qd[1], in[8], in[17], set_l, clr_l);
`endif
// Aggregate outputs into a single result vector
wire [63:0] result = {52'h0, 2'b0,qd, 4'b0, 2'b0,qm};
// wire [63:0] result = {52'h0, 2'b0,qd, 2'b0,ql, 2'b0,qm};
// Test loop
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result);
`endif
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]};
if (cyc==0) begin
// Setup
crc <= 64'h5aef0c8d_d70a4497;
sum <= 64'h0;
end
else if (cyc<10) begin
sum <= 64'h0;
end
else if (cyc<90) begin
end
else if (cyc==99) begin
$write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum);
if (crc !== 64'hc77bb9b3784ea091) $stop;
// What checksum will we end up with (above print should match)
// Note not all simulators agree about the latch result. Maybe have a race?
`define EXPECTED_SUM 64'hb73acf228acaeaa3
if (sum !== `EXPECTED_SUM) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
primitive udp_mux2 (z, a, b, sel);
output z;
input a, b, sel;
table
//a b s o
? 1 1 : 1 ;
? 0 1 : 0 ;
1 ? 0 : 1 ;
0 ? 0 : 0 ;
1 1 x : 1 ;
// Next blank line is intentional for parser
// Next \ at EOL is intentional for parser
0 0 x \
: 0 ;
endtable
endprimitive
primitive udp_latch (q, clk, d);
output q; reg q;
input clk, d;
table
//clk d q q'
0 1 : ? : 1;
0 0 : ? : 0;
1 ? : ? : -;
endtable
endprimitive
primitive udp_dff (q, clk, d, set_l, clr_l);
output q;
input clk, d, set_l, clr_l;
reg q;
table
//ck d s c : q : q'
r 0 1 ? : ? : 0 ;
r 1 ? 1 : ? : 1 ;
* 1 ? 1 : 1 : 1 ;
* 0 1 ? : 0 : 0 ;
f ? ? ? : ? : - ;
b * ? ? : ? : - ;
? ? 0 ? : ? : 1 ;
b ? * 1 : 1 : 1 ;
x 1 * 1 : 1 : 1 ;
? ? 1 0 : ? : 0 ;
b ? 1 * : 0 : 0 ;
x 0 1 * : 0 : 0 ;
endtable
endprimitive

View File

@ -0,0 +1,6 @@
%Error: t/t_udp_bad_comb_trigger.v:14:10: There should not be a edge trigger for combinational UDP table line
: ... note: In instance 'top'
14 | (01) 1 0 : 0;
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -9,11 +9,8 @@
import vltest_bootstrap
test.scenarios('vlt')
test.top_filename = "t/t_udp.v"
test.scenarios('linter')
test.lint(
# Unsupported: UDP Tables
verilator_flags2=["--lint-only --bbox-unsup"])
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
primitive t_gate(dout, a, b, c);
output dout;
input a, b, c;
table
x 0 1 : 1;
0 ? 1 : 1;
(01) 1 0 : 0;
1 1 ? : 1;
1 0 0 : 0;
0 0 0 : 1;
endtable
endprimitive
module top (o, a, b, c);
output o;
input a, b, c;
t_gate(o, a, b, c);
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_udp_bad_first_input.v:8:7: First UDP port must be the output port
: ... note: In instance 'top'
8 | input a, b, c;
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
primitive t_gate(a, b, c, dout);
input a, b, c;
output dout;
table
x 0 1 : 1;
0 ? 1 : 1;
0 1 0 : 0;
1 1 ? : 1;
1 0 0 : 0;
0 0 0 : 1;
endtable
endprimitive
module top (a, b, c, o);
input a, b, c;
output o;
t_gate(a, b, c, o);
endmodule

View File

@ -0,0 +1,30 @@
%Error: t/t_udp_bad_illegal_output.v:9:8: For sequential UDP, the output must be of 'reg' data type
: ... note: In instance 'top'
9 | output dout;
| ^~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_udp_bad_illegal_output.v:16:22: Illegal value for sequential UDP line output
: ... note: In instance 'top'
16 | 1 1 ? : ?: *;
| ^
%Error: t/t_udp_bad_illegal_output.v:17:11: There can be only one edge tigger signal
: ... note: In instance 'top'
17 | f r 0 : ?: 0;
| ^
%Error: t/t_udp_bad_illegal_output.v:18:22: Illegal value for sequential UDP line output
: ... note: In instance 'top'
18 | 0 0 0 : ?: *;
| ^
%Error: t/t_udp_bad_illegal_output.v:29:9: There should not be a edge trigger for combinational UDP table line
: ... note: In instance 'top'
29 | r ? 1 : 1;
| ^
%Error: t/t_udp_bad_illegal_output.v:31:20: Illegal value for combinational UDP line output
: ... note: In instance 'top'
31 | 1 1 ? : *;
| ^
%Error: t/t_udp_bad_illegal_output.v:33:20: Illegal value for combinational UDP line output
: ... note: In instance 'top'
33 | 0 0 0 : *;
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,43 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
primitive t_gate_comb(dout, a, b, c);
input a, b, c;
output dout;
table
r 0 1 : ?: 1;
r ? 1 : ?: 1;
r ? 0 : ?: 1;
0 1 0 : ?: 0;
1 1 ? : ?: *;
f r 0 : ?: 0;
0 0 0 : ?: *;
endtable
endprimitive
primitive t_gate_seq(dout, a, b, c);
input a, b, c;
output dout;
table
x 0 1 : 1;
r ? 1 : 1;
0 1 0 : 0;
1 1 ? : *;
1 0 0 : 0;
0 0 0 : *;
endtable
endprimitive
module top (a, b, c, o1, o2);
input a, b, c;
output o1, o2;
t_gate_comb(o1, a, b, c);
t_gate_seq(o2, a, b, c);
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_udp_bad_input_num.v:14:9: Incorrect number of input values, expected 3, got 2
: ... note: In instance 'top'
14 | 1 0 : 0;
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
primitive t_gate(dout, a, b, c);
output dout;
input a, b, c;
table
x 0 1 : 1;
0 ? 1 : 1;
1 0 : 0;
1 1 ? : 1;
1 0 0 : 0;
0 0 0 : 1;
endtable
endprimitive
module top (a, b, c, o);
input a, b, c;
output o;
t_gate(o, a, b, c);
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_udp_bad_multi_output.v:8:15: 2 output ports for UDP table, there must be one output port
: ... note: In instance 'top'
8 | output dout1, dout2;
| ^~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
primitive t_gate(dout1, dout2, a, b, c);
output dout1, dout2;
input a, b, c;
table
x 0 1 : 1;
0 ? 1 : 1;
0 1 0 : 0;
1 1 ? : 1;
1 0 0 : 0;
0 0 0 : 1;
endtable
endprimitive
module top (a, b, c, o1, o2);
input a, b, c;
output o1, o2;
t_gate(o1, o2, a, b, c);
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,66 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Mike Thyer.
// SPDX-License-Identifier: CC0-1.0
primitive d_edge_ff (q, clock, data);
output q; reg q;
input clock, data;
initial q = 1'b1;
table
// clock data q q+
// obtain output on rising edge of clock
F 0 : ? : 0 ;
(10) 1 : ? : 1 ;
R 0 : ? : 1 ;
(0?) 1 : ? : 0 ;
endtable
endprimitive
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
reg d, q;
d_edge_ff g (q, clk, d);
int cycle=0;
initial d = 0;
always @(posedge clk or negedge clk) begin
cycle <= cycle+1;
if (cycle==0) begin
d = 1;
end
else if (cycle==1) begin
d = 0;
if (q != 1) $stop;
end
else if (cycle==2) begin
if (q != 1) $stop;
end
else if (cycle==3) begin
if (q != 0) $stop;
end
else if (cycle==4) begin
d = 1;
if (q != 1) $stop;
end
else if (cycle==5) begin
$display("d = %d clk = %d cycle = %d", d, clk, cycle);
if (q != 1) $stop;
end
else if (cycle==6) begin
if (q != 0) $stop;
end
else if (cycle==7) begin
if (q != 1) $stop;
end
else if (cycle >= 8) begin
if (q != 0) $stop;;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_udp_sequential_bad.v:8:8: For combinational UDP, the output must not be a 'reg' data type
: ... note: In instance 'top'
8 | output dout;
| ^~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,27 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
primitive or_gate(dout, a, b, c);
output dout;
input a, b, c;
reg dout;
table
x 0 1 : 1;
0 ? 1 : 1;
0 1 0 : 0;
1 1 ? : 1;
1 0 0 : 0;
0 0 0 : 1;
endtable
endprimitive
module top (a, b, c, o);
input a, b, c;
output o;
or_gate(o, a, b, c);
endmodule