ivl 词法分析

看完了ivlpp预处理模块对文件的宏替换以及include展开后,来看一下iverilog接下来的处理步骤.

执行iverilog -v hello.v后,可以看到执行的方式是:

 /usr/local/lib/ivl/ivlpp  -v -L -F"/tmp/ivrlg274dae902" -f"/tmp/ivrlg74dae902" -p"/tmp/ivrli74dae902" | /usr/local/lib/ivl/ivl -v -C"/tmp/ivrlh74dae902" -C"/usr/local/lib/ivl/vvp.conf"

ivlpp部分已经在上一篇博客中做了简要的分析,接下来看ivl这个工具的流程,从参数可以看到有两个-C 的文件路径被传入,先看vvp.conf的内容

functor:cprop
functor:nodangle
flag:DLL=vvp.tgt
flag:VVP_EXECUTABLE=/usr/local/bin/vvp

看起来像是后续的调用链的描述

再看tmp/ivrlh74dae902

basedir:/usr/local/lib/ivl
module:/usr/local/lib/ivl/system.vpi
module:/usr/local/lib/ivl/vhdl_sys.vpi
module:/usr/local/lib/ivl/vhdl_textio.vpi
module:/usr/local/lib/ivl/v2005_math.vpi
module:/usr/local/lib/ivl/va_math.vpi
generation:2005
generation:no-specify
generation:assertions
generation:xtypes
generation:io-range-error
generation:no-strict-ca-eval
generation:no-strict-expr-width
generation:shared-loop-index
generation:no-verilog-ams
generation:icarus-misc
warnings:n
ignore_missing_modules:false
out:a.out
iwidth:32
widthcap:65536
ivlpp:/usr/local/lib/ivl/ivlpp  -L -F"/tmp/ivrlg23b9f55af" -P"/tmp/ivrli3b9f55af"

从内容上看也没有什么特别之处,进行了一些参数的传递

接下来主要分析./lexor.lex这个文件,这个lexor.lex通过两行%%,将整体文件划分为3部分,先看第一部分

第一部分由%{.....%}的C语言程序部分以及词法部分组成.c语言程序部分后续分析时根据调用关系再介绍,词法定义部分精炼:

%x CCOMMENT
%x PCOMMENT
%x LCOMMENT
%x CSTRING
%s UDPTABLE
%x PPTIMESCALE
%x PPUCDRIVE
%x PPDEFAULT_NETTYPE
%x PPBEGIN_KEYWORDS
%s EDGES
%x REAL_SCALE

W [ \t\b\f\r]+

S [afpnumkKMGT]

TU [munpf]

定义了一些起始状态和排他,词法的话只有3个,代表空格等无意义的控制字符集合W,两个未知的字母集合S 和TU


解读一些第二部分


  /* Recognize the various line directives. */
^"#line"[ \t]+.+ { line_directive(); }
^[ \t]?"`line"[ \t]+.+ { line_directive2(); }

[ \t\b\f\r] { ; }
\n { yylloc.first_line += 1; }

首先是对#line和`line的匹配,这里的#line的作用是改变编译器对__FILE__ 和 __LINE__的值,因为在ivlpp中,对`include的处理导致所有的文件加载到了一起,若在此时再发现语法错误,在报错时无法正确的输出文件名和行号,因此通过#line的方式修改__FILE__和 __LINE__ 以增加报错信息的可读性

从ivlpp输出的中间文件可以看出这个标记

`line 23 "./ivltests/include1.v" 2

`line 25 "./ivltests/include2.v" 2

reg [7:0] a,b;

initial begin
   a = 10'hff;
   b = 8'hff  -1;
   end

在line_directive()中,识别完行号与文件名后,执行了

      yylloc.text = set_file_name(buf);
      yylloc.first_line = lineno;

相当于将文件名和行号进行了重新赋值

[ \t\b\f\r] { ; }
\n { yylloc.first_line += 1; }

跳过空白块,遇到换行符时将first_line加一


"//"{W}*"synthesis"{W}+"translate_on"{W}*\n { pform_mc_translate_on(true); }
"//"{W}*"synthesis"{W}+"translate_off"{W}*\n { pform_mc_translate_on(false); }
"//" { comment_enter = YY_START; BEGIN(LCOMMENT); }
<LCOMMENT>.    { yymore(); }
<LCOMMENT>\n   { yylloc.first_line += 1; BEGIN(comment_enter); }


  /* The contents of C-style comments are ignored, like white space. */

"/*" { comment_enter = YY_START; BEGIN(CCOMMENT); }
<CCOMMENT>.    { ; }
<CCOMMENT>\n   { yylloc.first_line += 1; }
<CCOMMENT>"*/" { BEGIN(comment_enter); }

在HDL语言中,synthesis translate_on和translate_off用于显示的控制编译器是否编译.当遇到off时,向pform_mc_translate_on中传入false,否则传入true,最终影响此分支的执行效果

      if (pform_mc_translate_flag == false) {
	    if (attr == 0) attr = new list<named_pexpr_t>;
	    named_pexpr_t tmp;
	    tmp.name = perm_string::literal("ivl_synthesis_off");
	    tmp.parm = 0;
	    attr->push_back(tmp);
      }

对于两种模式的注释,//型的进入LCOMMENT状态处理,yymore()表示不清空yytext中的内容,因此,yymore()会造成yytext的积累,这种积累直到遇到换行符时结束,并恢复至初始状态

对于块注释,处理方式类似.


"(*" { return K_PSTAR; }
"*)" { return K_STARP; }
".*" { return K_DOTSTAR; }
"<<" { return K_LS; }
"<<<" { return K_LS; /* Note: Functionally, <<< is the same as <<. */}
">>"  { return K_RS; }
">>>" { return K_RSS; }
"**" { return K_POW; }
"<=" { return K_LE; }
">=" { return K_GE; }
"=>" { return K_EG; }
"*>" { return K_SG; }
"==" { return K_EQ; }
"!=" { return K_NE; }
"===" { return K_CEQ; }
"!==" { return K_CNE; }
"==?" { return K_WEQ; }
"!=?" { return K_WNE; }
"||" { return K_LOR; }
"&&" { return K_LAND; }
"&&&" { return K_TAND; }
"~|" { return K_NOR; }
"~^" { return K_NXOR; }
"^~" { return K_NXOR; }
"~&" { return K_NAND; }
"->" { return K_TRIGGER; }
"+:" { return K_PO_POS; }
"-:" { return K_PO_NEG; }
"<+" { return K_CONTRIBUTE; }
"+=" { return K_PLUS_EQ; }
"-=" { return K_MINUS_EQ; }
"*=" { return K_MUL_EQ; }
"/=" { return K_DIV_EQ; }
"%=" { return K_MOD_EQ; }
"&=" { return K_AND_EQ; }
"|=" { return K_OR_EQ; }
"^=" { return K_XOR_EQ; }
"<<=" { return K_LS_EQ; }
">>=" { return K_RS_EQ; }
"<<<=" { return K_LS_EQ; }
">>>=" { return K_RSS_EQ; }
"++" { return K_INCR; }
"--" {return K_DECR; }
"'{" { return K_LP; }
"::" { return K_SCOPE_RES; }

这里是代码中的各种计算符号

"+=>"|"-=>"	{
			/*
			 * Resolve the ambiguity between the += assignment
			 * operator and +=> polarity edge path operator
			 *
			 * +=> should be treated as two separate tokens '+' and
			 * '=>' (K_EG), therefore we only consume the first
			 * character of the matched pattern i.e. either + or -
			 * and push back the rest of the matches text (=>) in
			 * the input stream.
			 */
			yyless(1);
			return yytext[0];
		}

值得一提的是,+=>和-=> 这里可能造成+= > 以及 +  =>两种解读方式的归约冲突.而正确的解读方式是+   =>因此将=>回送到输入流中,只返回yytext[0]

 /* Watch out for the tricky case of (*). Cannot parse this as "(*"
     and ")", but since I know that this is really ( * ), replace it
     with "*" and return that. */
"("{W}*"*"{W}*")" { return '*'; }

<EDGES>"]" { BEGIN(0); return yytext[0]; }
[}{;:\[\],()#=.@&!?<>%|^~+*/-] { return yytext[0]; }

对于(*)的情况,返回*,这里突然插入了一个<EDGES>为起始状态,对]处理的规则,与下一条规则的区别在于将状态返回至INITIAL状态.

\"            { BEGIN(CSTRING); }
<CSTRING>\\\\ { yymore(); /* Catch \\, which is a \ escaping itself */ }
<CSTRING>\\\" { yymore(); /* Catch \", which is an escaped quote */ }
<CSTRING>\n   { BEGIN(0);
                yylval.text = yytext_string_filter(yytext, yyleng);
		VLerror(yylloc, "Missing close quote of string.");
		yylloc.first_line += 1;
		return STRING; }
<CSTRING>\"   { BEGIN(0);
      yylval.text = yytext_string_filter(yytext, yyleng);
		yylval.text[strlen(yytext)-1] = 0;
		return STRING; }
<CSTRING>.    { yymore(); }

用于处理字符串,遇到"开始,再遇到"结束,对于字符串中出现的转义符和转义的"进行追加处理.

/* The UDP Table is a unique lexical environment. These are most
     tokens that we can expect in a table. */
<UDPTABLE>\(\?0\)    { return '_'; }
<UDPTABLE>\(\?1\)    { return '+'; }
<UDPTABLE>\(\?[xX]\) { return '%'; }
<UDPTABLE>\(\?\?\)  { return '*'; }
<UDPTABLE>\(01\)    { return 'r'; }
<UDPTABLE>\(0[xX]\) { return 'Q'; }
<UDPTABLE>\(b[xX]\) { return 'q'; }
<UDPTABLE>\(b0\)    { return 'f'; /* b0 is 10|00, but only 10 is meaningful */}
<UDPTABLE>\(b1\)    { return 'r'; /* b1 is 11|01, but only 01 is meaningful */}
<UDPTABLE>\(0\?\)   { return 'P'; }
<UDPTABLE>\(10\)    { return 'f'; }
<UDPTABLE>\(1[xX]\) { return 'M'; }
<UDPTABLE>\(1\?\)   { return 'N'; }
<UDPTABLE>\([xX]0\) { return 'F'; }
<UDPTABLE>\([xX]1\) { return 'R'; }
<UDPTABLE>\([xX]\?\) { return 'B'; }
<UDPTABLE>[bB]     { return 'b'; }
<UDPTABLE>[lL]     { return 'l'; /* IVL extension */ }
<UDPTABLE>[hH]     { return 'h'; /* IVL extension */ }
<UDPTABLE>[fF]     { return 'f'; }
<UDPTABLE>[rR]     { return 'r'; }
<UDPTABLE>[xX]     { return 'x'; }
<UDPTABLE>[nN]     { return 'n'; }
<UDPTABLE>[pP]     { return 'p'; }
<UDPTABLE>[01\?\*\-:;] { return yytext[0]; }

<EDGES>"01" { return K_edge_descriptor; }
<EDGES>"0x" { return K_edge_descriptor; }
<EDGES>"0z" { return K_edge_descriptor; }
<EDGES>"10" { return K_edge_descriptor; }
<EDGES>"1x" { return K_edge_descriptor; }
<EDGES>"1z" { return K_edge_descriptor; }
<EDGES>"x0" { return K_edge_descriptor; }
<EDGES>"x1" { return K_edge_descriptor; }
<EDGES>"z0" { return K_edge_descriptor; }
<EDGES>"z1" { return K_edge_descriptor; }

在verilog HDL中,有一种只用于仿真的语法,UDP, 具体的介绍可以看这个博客UDP介绍 这是是对UPD table的解析

[a-zA-Z_][a-zA-Z0-9$_]* {
      int rc = lexor_keyword_code(yytext, yyleng);
      switch (rc) {
	  case IDENTIFIER:
	    yylval.text = strdupnew(yytext);
	    if (strncmp(yylval.text,"PATHPULSE$", 10) == 0)
		  rc = PATHPULSE_IDENTIFIER;
	    break;

	  case K_edge:
	    BEGIN(EDGES);
	    break;

	  case K_module:
	  case K_macromodule:
	    in_module = true;
	    break;

	  case K_endmodule:
	    in_module = false;
	    break;

	  case K_primitive:
	    in_UDP = true;
	    break;

	  case K_endprimitive:
	    in_UDP = false;
	    break;

	  case K_table:
	    BEGIN(UDPTABLE);
	    break;

	  default:
	    yylval.text = 0;
	    break;
      }

	/* Special case: If this is part of a scoped name, then check
	   the package for identifier details. For example, if the
	   source file is  foo::bar, the parse.y will note the
	   PACKAGE_IDENTIFIER and "::" token and mark the
	   "in_package_scope" variable. Then this lexor will see the
	   identifier here and interpret it in the package scope. */
      if (in_package_scope) {
	    if (rc == IDENTIFIER) {
		  if (data_type_t*type = pform_test_type_identifier(in_package_scope, yylval.text)) {
			yylval.type_identifier.text = yylval.text;
			yylval.type_identifier.type = type;
			rc = TYPE_IDENTIFIER;
		  }
	    }
	    in_package_scope = 0;
	    return rc;
      }

	/* If this identifier names a discipline, then return this as
	   a DISCIPLINE_IDENTIFIER and return the discipline as the
	   value instead. */
      if (rc == IDENTIFIER && gn_verilog_ams_flag) {
	    perm_string tmp = lex_strings.make(yylval.text);
	    map<perm_string,ivl_discipline_t>::iterator cur = disciplines.find(tmp);
	    if (cur != disciplines.end()) {
		  delete[]yylval.text;
		  yylval.discipline = (*cur).second;
		  rc = DISCIPLINE_IDENTIFIER;
	    }
      }

	/* If this identifier names a previously declared package, then
	   return this as a PACKAGE_IDENTIFIER instead. */
      if (rc == IDENTIFIER && gn_system_verilog()) {
	    if (PPackage*pkg = pform_test_package_identifier(yylval.text)) {
		  delete[]yylval.text;
		  yylval.package = pkg;
		  rc = PACKAGE_IDENTIFIER;
	    }
      }

	/* If this identifier names a previously declared type, then
	   return this as a TYPE_IDENTIFIER instead. */
      if (rc == IDENTIFIER && gn_system_verilog()) {
	    if (data_type_t*type = pform_test_type_identifier(yylloc, yylval.text)) {
		  yylval.type_identifier.text = yylval.text;
		  yylval.type_identifier.type = type;
		  rc = TYPE_IDENTIFIER;
	    }
      }

      return rc;
  }

接着开始对变量进行匹配.对于匹配到的yytext,先传入lexor_keyword_code进行处理.

int lexor_keyword_code(const char*str, unsigned nstr)
{
      const struct lexor_keyword*rc = Lkwd::check_identifier(str, nstr);
      if (rc == 0)
	  return IDENTIFIER;
      else if ((rc->mask & lexor_keyword_mask) == 0)
          return IDENTIFIER;
      else
	  return rc->tokenType;
}

check_identifier用于通过gprof产生的哈希表来判断该参数是否是保留字,若是保留字则返回tokenType,否则返回IDENTIFIER,用于该字是标识符.若是标识符,则将yylval.text赋值为yytext,否则若yytext是edge,则rc为K_edge,此时进入EDGE状态.同理,在switch中对于module,primitive,table进行了状态或标志为的配置.对于其他保留字,没有进行处理.

若标识符是foo::bar形式,则在处理foo时,in_package_scrope不为0,则匹配yytext是否是当前结构体的子变量,若是,则返回TYPE_IDENTIFIER.后续的几个分支是对于不同的verilogHDL 的语法标准下进行检测的.



\\[^ \t\b\f\r\n]+         {
      yylval.text = strdupnew(yytext+1);
      if (gn_system_verilog()) {
	    if (PPackage*pkg = pform_test_package_identifier(yylval.text)) {
		  delete[]yylval.text;
		  yylval.package = pkg;
		  return PACKAGE_IDENTIFIER;
	    }
      }
      if (gn_system_verilog()) {
	    if (data_type_t*type = pform_test_type_identifier(yylloc, yylval.text)) {
		  yylval.type_identifier.text = yylval.text;
		  yylval.type_identifier.type = type;
		  return TYPE_IDENTIFIER;
	    }
      }
      return IDENTIFIER;
  }

\$([a-zA-Z0-9$_]+)        {
	/* The 1364-1995 timing checks. */
      if (strcmp(yytext,"$hold") == 0)
	    return K_Shold;
      if (strcmp(yytext,"$nochange") == 0)
	    return K_Snochange;
      if (strcmp(yytext,"$period") == 0)
	    return K_Speriod;
      if (strcmp(yytext,"$recovery") == 0)
	    return K_Srecovery;
      if (strcmp(yytext,"$setup") == 0)
	    return K_Ssetup;
      if (strcmp(yytext,"$setuphold") == 0)
	    return K_Ssetuphold;
      if (strcmp(yytext,"$skew") == 0)
	    return K_Sskew;
      if (strcmp(yytext,"$width") == 0)
	    return K_Swidth;
	/* The new 1364-2001 timing checks. */
      if (strcmp(yytext,"$fullskew") == 0)
	    return K_Sfullskew;
      if (strcmp(yytext,"$recrem") == 0)
	    return K_Srecrem;
      if (strcmp(yytext,"$removal") == 0)
	    return K_Sremoval;
      if (strcmp(yytext,"$timeskew") == 0)
	    return K_Stimeskew;

      if (strcmp(yytext,"$attribute") == 0)
	    return KK_attribute;

      if (gn_system_verilog() && strcmp(yytext,"$unit") == 0) {
	    yylval.package = pform_units.back();
	    return PACKAGE_IDENTIFIER;
      }

      yylval.text = strdupnew(yytext);
      return SYSTEM_IDENTIFIER; }

对于标识符,可以使用reg \abe这种形式,在verilog中叫转义标识符,一般的标识符对使用到的字符有限制,转义标识符可以使用所有可显示的ASCII字符。转字标识符必须以’\'为开头以空格、Tab键盘、换行其中之一为结尾。如果转义标识符中没有用到其它特殊字符,则本质上仍然是一般的标识符。上面的例子中,定义时为 \abe,使用时直接用 abe 即可。

有个特殊情况,就是转义字符直接使用Verilog HDL中保留的关键字,如上面的 \always,不过使用时前面的’\'不能省略。

接下来是以$开头的词,在verilog 中,$通常用于调用一些在仿真的系统功能函数,这里对一些函数做了匹配,其余的函数返回SYSTEM_IDENTIFIER

\'[sS]?[dD][ \t]*[0-9][0-9_]* {
      yylval.number = make_unsized_dec(yytext);
      return BASED_NUMBER;
}
\'[sS]?[dD][ \t]*[xzXZ?]_* {
      yylval.number = make_undef_highz_dec(yytext);
      return BASED_NUMBER;
}
\'[sS]?[bB][ \t]*[0-1xzXZ?][0-1xzXZ?_]* {
      yylval.number = make_unsized_binary(yytext);
      return BASED_NUMBER;
}
\'[sS]?[oO][ \t]*[0-7xzXZ?][0-7xzXZ?_]* {
      yylval.number = make_unsized_octal(yytext);
      return BASED_NUMBER;
}
\'[sS]?[hH][ \t]*[0-9a-fA-FxzXZ?][0-9a-fA-FxzXZ?_]* {
      yylval.number = make_unsized_hex(yytext);
      return BASED_NUMBER;
}
\'[01xzXZ] {
      if (!gn_system_verilog()) {
	    cerr << yylloc.text << ":" << yylloc.first_line << ": warning: "
		 << "Using SystemVerilog 'N bit vector.  Use at least "
		 << "-g2005-sv to remove this warning." << endl;
      }
      generation_t generation_save = generation_flag;
      generation_flag = GN_VER2005_SV;
      yylval.number = make_unsized_binary(yytext);
      generation_flag = generation_save;
      return UNBASED_NUMBER; }

  /* Decimal numbers are the usual. But watch out for the UDPTABLE
     mode, where there are no decimal numbers. Reject the match if we
     are in the UDPTABLE state. */
[0-9][0-9_]* {
      if (YY_START==UDPTABLE) {
	    REJECT;
      } else {
	    yylval.number = make_unsized_dec(yytext);
	    based_size = yylval.number->as_ulong();
	    return DEC_NUMBER;
      }
}

  /* This rule handles scaled time values for SystemVerilog. */
[0-9][0-9_]*(\.[0-9][0-9_]*)?{TU}?s {
      if (gn_system_verilog()) {
	    yylval.text = strdupnew(yytext);
	    return TIME_LITERAL;
      } else REJECT; }

这部分用于处理数字.第一种情况匹配没有标识位宽的10进制数字,第二种用于匹配没有标识位宽的以x,z开头的数字

第三种用于匹配没有标识位宽的带有x,z的二进制数字.第四种用于匹配没有进行标识位宽的8进制数字,第五种用于匹配没有标识位宽的16进制数字,第六种用于匹配二进制数字,但没有注明进制,这里进行了warm,若没有使用sv-2005及其以后的标准,这样做是有风险的.

对最基本的数字,在第七种模式下进行十进制匹配,但在UDPTABLE中,没有十进制数,因此REJECT,在UDPTABLE中继续查找符合的规则;最后一种模式用于处理小数精度的时间度量,如1.23ms,{TU}中为munpf,与后面的s组合成不同的时间精度

  /* These rules handle the scaled real literals from Verilog-AMS. The
     value is a number with a single letter scale factor. If
     verilog-ams is not enabled, then reject this rule. If it is
     enabled, then collect the scale and use it to scale the value. */
[0-9][0-9_]*\.[0-9][0-9_]*/{S} {
      if (!gn_verilog_ams_flag) REJECT;
      BEGIN(REAL_SCALE);
      yymore();  }

[0-9][0-9_]*/{S} {
      if (!gn_verilog_ams_flag) REJECT;
      BEGIN(REAL_SCALE);
      yymore();  }

<REAL_SCALE>{S} {
      size_t token_len = strlen(yytext);
      char*tmp = new char[token_len + 5];
      int scale = 0;
      strcpy(tmp, yytext);
      switch (tmp[token_len-1]) {
	  case 'a': scale = -18; break; /* atto- */
	  case 'f': scale = -15; break; /* femto- */
	  case 'p': scale = -12; break; /* pico- */
	  case 'n': scale = -9;  break; /* nano- */
	  case 'u': scale = -6;  break; /* micro- */
	  case 'm': scale = -3;  break; /* milli- */
	  case 'k': scale = 3;  break; /* kilo- */
	  case 'K': scale = 3;  break; /* kilo- */
	  case 'M': scale = 6;  break; /* mega- */
	  case 'G': scale = 9;  break; /* giga- */
	  case 'T': scale = 12; break; /* tera- */
	  default: assert(0); break;
      }
      snprintf(tmp+token_len-1, 5, "e%d", scale);
      yylval.realtime = new verireal(tmp);
      delete[]tmp;

      BEGIN(0);
      return REALTIME;  }

这里用于处理一种类似于科学计数法的数字表示,如:

1.0a = 1.0e-18

1.0f  = 1.0e-15

1.0p  = 1.0e-12
 


[0-9][0-9_]*\.[0-9][0-9_]*([Ee][+-]?[0-9][0-9_]*)? {
      yylval.realtime = new verireal(yytext);
      return REALTIME; }

[0-9][0-9_]*[Ee][+-]?[0-9][0-9_]* {
      yylval.realtime = new verireal(yytext);
      return REALTIME; }

这里用于匹配浮点数



  /* Notice and handle the `timescale directive. */

^{W}?`timescale { BEGIN(PPTIMESCALE); }
<PPTIMESCALE>.* { process_timescale(yytext); }
<PPTIMESCALE>\n {
      if (in_module) {
	    cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
		    "`timescale directive can not be inside a module "
		    "definition." << endl;
	    error_count += 1;
      }
      yylloc.first_line += 1;
      BEGIN(0); }

  /* Notice and handle the `celldefine and `endcelldefine directives. */

^{W}?`celldefine{W}?    { in_celldefine = true; }
^{W}?`endcelldefine{W}? { in_celldefine = false; }

  /* Notice and handle the resetall directive. */

^{W}?`resetall{W}? {
      if (in_module) {
	    cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
		    "`resetall directive can not be inside a module "
		    "definition." << endl;
	    error_count += 1;
      } else if (in_UDP) {
	    cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
		    "`resetall directive can not be inside a UDP "
		    "definition." << endl;
	    error_count += 1;
      } else {
	    reset_all();
      } }
 /* Notice and handle the `unconnected_drive directive. */
^{W}?`unconnected_drive { BEGIN(PPUCDRIVE); }
<PPUCDRIVE>.* { process_ucdrive(yytext); }
<PPUCDRIVE>\n {
      if (in_module) {
	    cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
		    "`unconnected_drive directive can not be inside a "
		    "module definition." << endl;
	    error_count += 1;
      }
      yylloc.first_line += 1;
      BEGIN(0); }

^{W}?`nounconnected_drive{W}? {
      if (in_module) {
	    cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
		    "`nounconnected_drive directive can not be inside a "
		    "module definition." << endl;
	    error_count += 1;
      }
      uc_drive = UCD_NONE; }

用于处理`timescale `celldefine `endcelldefine `resetall,这些定义不可以出现在module和 UDP 内部,

reset_all用于复位时间精度

这里提到了unconnected_driver和celldefine 目前还没有使用过,从其他博客中找到的解释

`unconnected_drive和`nounconnected_drive

  在模块实例化中,出现在这两个编译器指令间的任何未连接的输入端口或者为正偏电路状态或者为反偏电路状态。

`unconnected_drive pull1
. . .

`nounconnected_drive

`unconnected_drive pull0
. . .

`nounconnected_drive

`celldefine 和`endcelldefine

  这两个程序指令用于将模块标记为单元模块。它们表示包含模块定义,如下例所示。

`celldefine
module FD1S3AX (D, CK, Z) ;
. . .
endmodule
`endcelldefine

  某些PLI例程使用单元模块。

 

具体作用未知



  /* These are directives that I do not yet support. I think that IVL
     should handle these, not an external preprocessor. */
  /* From 1364-2005 Chapter 19. */
^{W}?`pragme{W}?.*                  {  }

  /* From 1364-2005 Annex D. */
^{W}?`default_decay_time{W}?.*      {  }
^{W}?`default_trireg_strength{W}?.* {  }
^{W}?`delay_mode_distributed{W}?.*  {  }
^{W}?`delay_mode_path{W}?.*         {  }
^{W}?`delay_mode_unit{W}?.*         {  }
^{W}?`delay_mode_zero{W}?.*         {  }

  /* From other places. */
^{W}?`disable_portfaults{W}?.*      {  }
^{W}?`enable_portfaults{W}?.*       {  }
`endprotect                         {  }
^{W}?`nosuppress_faults{W}?.*       {  }
`protect                            {  }
^{W}?`suppress_faults{W}?.*         {  }
^{W}?`uselib{W}?.*                  {  }

这里列举了一些目前还没有支持的保留字

^{W}?`begin_keywords{W}? { BEGIN(PPBEGIN_KEYWORDS); }

<PPBEGIN_KEYWORDS>\"[a-zA-Z0-9 -\.]*\".* {
      keyword_mask_stack.push_front(lexor_keyword_mask);

      char*word = yytext+1;
      char*tail = strchr(word, '"');
      tail[0] = 0;
      if (strcmp(word,"1364-1995") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995;
      } else if (strcmp(word,"1364-2001") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001
		                |GN_KEYWORDS_1364_2001_CONFIG;
      } else if (strcmp(word,"1364-2001-noconfig") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001;
      } else if (strcmp(word,"1364-2005") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001
		                |GN_KEYWORDS_1364_2001_CONFIG
		                |GN_KEYWORDS_1364_2005;
      } else if (strcmp(word,"1800-2005") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001
		                |GN_KEYWORDS_1364_2001_CONFIG
		                |GN_KEYWORDS_1364_2005
		                |GN_KEYWORDS_1800_2005;
      } else if (strcmp(word,"1800-2009") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001
		                |GN_KEYWORDS_1364_2001_CONFIG
		                |GN_KEYWORDS_1364_2005
		                |GN_KEYWORDS_1800_2005
		                |GN_KEYWORDS_1800_2009;
      } else if (strcmp(word,"1800-2012") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001
		                |GN_KEYWORDS_1364_2001_CONFIG
		                |GN_KEYWORDS_1364_2005
		                |GN_KEYWORDS_1800_2005
		                |GN_KEYWORDS_1800_2009
		                |GN_KEYWORDS_1800_2012;
      } else if (strcmp(word,"VAMS-2.3") == 0) {
	    lexor_keyword_mask = GN_KEYWORDS_1364_1995
		                |GN_KEYWORDS_1364_2001
		                |GN_KEYWORDS_1364_2001_CONFIG
		                |GN_KEYWORDS_1364_2005
		                |GN_KEYWORDS_VAMS_2_3;
      } else {
	    fprintf(stderr, "%s:%d: Ignoring unknown keywords string: %s\n",
		    yylloc.text, yylloc.first_line, word);
      }
      BEGIN(0);
 }

<PPBEGIN_KEYWORDS>.* {
      fprintf(stderr, "%s:%d: Malformed keywords specification: %s\n",
	      yylloc.text, yylloc.first_line, yytext);
      BEGIN(0);
 }

^{W}?`end_keywords{W}?.* {
      if (!keyword_mask_stack.empty()) {
	    lexor_keyword_mask = keyword_mask_stack.front();
	    keyword_mask_stack.pop_front();
      } else {
	    fprintf(stderr, "%s:%d: Mismatched end_keywords directive\n",
		    yylloc.text, yylloc.first_line);
      }
 }

begin_keywords这里 在verilog语法中没有看到定义,也没有找到ivtest中对应的例子,看代码感觉像是在设置处理保留字是的mask值,用以标志当前的编译器支持哪些词法和语法.


`default_nettype{W}? { BEGIN(PPDEFAULT_NETTYPE); }
<PPDEFAULT_NETTYPE>.* {
      NetNet::Type net_type;
      size_t wordlen = strcspn(yytext, " \t\f\r\n");
      yytext[wordlen] = 0;
  /* Add support for other wire types and better error detection. */
      if (strcmp(yytext,"wire") == 0) {
	    net_type = NetNet::WIRE;

      } else if (strcmp(yytext,"tri") == 0) {
	    net_type = NetNet::TRI;

      } else if (strcmp(yytext,"tri0") == 0) {
	    net_type = NetNet::TRI0;

      } else if (strcmp(yytext,"tri1") == 0) {
	    net_type = NetNet::TRI1;

      } else if (strcmp(yytext,"wand") == 0) {
	    net_type = NetNet::WAND;

      } else if (strcmp(yytext,"triand") == 0) {
	    net_type = NetNet::TRIAND;

      } else if (strcmp(yytext,"wor") == 0) {
	    net_type = NetNet::WOR;

      } else if (strcmp(yytext,"trior") == 0) {
	    net_type = NetNet::TRIOR;

      } else if (strcmp(yytext,"none") == 0) {
	    net_type = NetNet::NONE;

      } else {
	    cerr << yylloc.text << ":" << yylloc.first_line
		 << ": error: Net type " << yytext
		 << " is not a valid (or supported)"
		 << " default net type." << endl;
	    net_type = NetNet::WIRE;
	    error_count += 1;
      }
      pform_set_default_nettype(net_type, yylloc.text, yylloc.first_line);
  }
<PPDEFAULT_NETTYPE>\n {
      yylloc.first_line += 1;
      BEGIN(0); }

这里是对`default_nettype的匹配,default_nettype用于设置没有进行类型修饰的标识符的默认类型,可选的参数有wire,tri,tri0,tri1,wand,triand,wor,trior,none

最后是重新匹配`define,`ifdef,`undef之类的宏控制字,因为这些字符在ivlpp中已经被替换过,所以不会被匹配到,若匹配到则说明发生了错误.


ivl部分的词法看起来比ivlpp的逻辑上要简单一些,但里面的返回状态更加复杂,后续会解读一下ivl的语法部分,词法和语法的解读应该可以帮助我了解iverilog整体的编译流程.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值