/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * * --- * * A simple lexer for Verilog code. Non-preprocessor compiler directives are * handled here. The preprocessor stuff is handled in preproc.cc. Everything * else is left to the bison parser (see parser.y). * */ %{ #ifdef __clang__ // bison generates code using the 'register' storage class specifier #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include "kernel/log.h" #include "verilog_frontend.h" #include "frontends/ast/ast.h" #include "verilog_parser.tab.h" USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { std::vector fn_stack; std::vector ln_stack; } YOSYS_NAMESPACE_END #define SV_KEYWORD(_tok) \ if (sv_mode) return _tok; \ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ "recognized unless read_verilog is called with -sv!\n", yytext, \ AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); \ return TOK_ID; #define YY_INPUT(buf,result,max_size) \ result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size) %} %option yylineno %option noyywrap %option nounput %option prefix="frontend_verilog_yy" %x COMMENT %x STRING %x SYNOPSYS_TRANSLATE_OFF %x SYNOPSYS_FLAGS %x IMPORT_DPI %% "`file_push "[^\n]* { fn_stack.push_back(current_filename); ln_stack.push_back(frontend_verilog_yyget_lineno()); current_filename = yytext+11; frontend_verilog_yyset_lineno(0); } "`file_pop"[^\n]*\n { current_filename = fn_stack.back(); fn_stack.pop_back(); frontend_verilog_yyset_lineno(ln_stack.back()); ln_stack.pop_back(); } "`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n { char *p = yytext + 5; while (*p == ' ' || *p == '\t') p++; frontend_verilog_yyset_lineno(atoi(p)); while (*p && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; char *q = *p ? p + 1 : p; while (*q && *q != '"') q++; current_filename = std::string(p).substr(1, q-p-1); } "`file_notfound "[^\n]* { log_error("Can't open include file `%s'!\n", yytext + 15); } "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ "`default_nettype"[ \t]+[^ \t\r\n/]+ { char *p = yytext; while (*p != 0 && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; if (!strcmp(p, "none")) VERILOG_FRONTEND::default_nettype_wire = false; else if (!strcmp(p, "wire")) VERILOG_FRONTEND::default_nettype_wire = true; else frontend_verilog_yyerror("Unsupported default nettype: %s", p); } "`"[a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); } "module" { return TOK_MODULE; } "endmodule" { return TOK_ENDMODULE; } "function" { return TOK_FUNCTION; } "endfunction" { return TOK_ENDFUNCTION; } "task" { return TOK_TASK; } "endtask" { return TOK_ENDTASK; } "parameter" { return TOK_PARAMETER; } "localparam" { return TOK_LOCALPARAM; } "defparam" { return TOK_DEFPARAM; } "assign" { return TOK_ASSIGN; } "always" { return TOK_ALWAYS; } "initial" { return TOK_INITIAL; } "begin" { return TOK_BEGIN; } "end" { return TOK_END; } "if" { return TOK_IF; } "else" { return TOK_ELSE; } "for" { return TOK_FOR; } "posedge" { return TOK_POSEDGE; } "negedge" { return TOK_NEGEDGE; } "or" { return TOK_OR; } "case" { return TOK_CASE; } "casex" { return TOK_CASEX; } "casez" { return TOK_CASEZ; } "endcase" { return TOK_ENDCASE; } "default" { return TOK_DEFAULT; } "generate" { return TOK_GENERATE; } "endgenerate" { return TOK_ENDGENERATE; } "while" { return TOK_WHILE; } "repeat" { return TOK_REPEAT; } "always_comb" { SV_KEYWORD(TOK_ALWAYS); } "always_ff" { SV_KEYWORD(TOK_ALWAYS); } "always_latch" { SV_KEYWORD(TOK_ALWAYS); } "assert" { SV_KEYWORD(TOK_ASSERT); } "property" { SV_KEYWORD(TOK_PROPERTY); } "logic" { SV_KEYWORD(TOK_REG); } "bit" { SV_KEYWORD(TOK_REG); } "input" { return TOK_INPUT; } "output" { return TOK_OUTPUT; } "inout" { return TOK_INOUT; } "wire" { return TOK_WIRE; } "reg" { return TOK_REG; } "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } "genvar" { return TOK_GENVAR; } "real" { return TOK_REAL; } [0-9][0-9_]* { frontend_verilog_yylval.string = new std::string(yytext); return TOK_CONST; } [0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_CONST; } [0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? { frontend_verilog_yylval.string = new std::string(yytext); return TOK_REALVAL; } [0-9][0-9_]*[eE][-+]?[0-9_]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_REALVAL; } \" { BEGIN(STRING); } \\. { yymore(); } \" { BEGIN(0); char *yystr = strdup(yytext); yystr[strlen(yytext) - 1] = 0; int i = 0, j = 0; while (yystr[i]) { if (yystr[i] == '\\' && yystr[i + 1]) { i++; if (yystr[i] == 'n') yystr[i] = '\n'; else if (yystr[i] == 't') yystr[i] = '\t'; else if ('0' <= yystr[i] && yystr[i] <= '7') { yystr[i] = yystr[i] - '0'; if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; i++; } if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; i++; } } } yystr[j++] = yystr[i++]; } yystr[j] = 0; frontend_verilog_yylval.string = new std::string(yystr); free(yystr); return TOK_STRING; } . { yymore(); } and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { frontend_verilog_yylval.string = new std::string(yytext); return TOK_PRIMITIVE; } supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } "$"(display|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; } "$signed" { return TOK_TO_SIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; } [a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); return TOK_ID; } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { log_warning("Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n" "Yosys does support them but it is recommended to use `ifdef constructs instead!\n"); printed_warning = true; } BEGIN(SYNOPSYS_TRANSLATE_OFF); } . /* ignore synopsys translate_off body */ \n /* ignore synopsys translate_off body */ "/*"[ \t]*(synopsys|synthesis)[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); } "/*"[ \t]*(synopsys|synthesis)[ \t]+ { BEGIN(SYNOPSYS_FLAGS); } full_case { static bool printed_warning = false; if (!printed_warning) { log_warning("Found one of those horrible `(synopsys|synthesis) full_case' comments.\n" "Yosys does support them but it is recommended to use verilog `full_case' attributes instead!\n"); printed_warning = true; } return TOK_SYNOPSYS_FULL_CASE; } parallel_case { static bool printed_warning = false; if (!printed_warning) { log_warning("Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n" "Yosys does support them but it is recommended to use verilog `parallel_case' attributes instead!\n"); printed_warning = true; } return TOK_SYNOPSYS_PARALLEL_CASE; } . /* ignore everything else */ "*/" { BEGIN(0); } import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { BEGIN(IMPORT_DPI); return TOK_DPI_FUNCTION; } [a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); return TOK_ID; } [ \t\r\n] /* ignore whitespaces */ ";" { BEGIN(0); return *yytext; } . { return *yytext; } "\\"[^ \t\r\n]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; } "(*" { return ATTR_BEGIN; } "*)" { return ATTR_END; } "{*" { return DEFATTR_BEGIN; } "*}" { return DEFATTR_END; } "**" { return OP_POW; } "||" { return OP_LOR; } "&&" { return OP_LAND; } "==" { return OP_EQ; } "!=" { return OP_NE; } "<=" { return OP_LE; } ">=" { return OP_GE; } "===" { return OP_EQX; } "!==" { return OP_NEX; } "~&" { return OP_NAND; } "~|" { return OP_NOR; } "~^" { return OP_XNOR; } "^~" { return OP_XNOR; } "<<" { return OP_SHL; } ">>" { return OP_SHR; } "<<<" { return OP_SSHL; } ">>>" { return OP_SSHR; } "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } "/*" { BEGIN(COMMENT); } . /* ignore comment body */ \n /* ignore comment body */ "*/" { BEGIN(0); } [ \t\r\n] /* ignore whitespaces */ \\[\r\n] /* ignore continuation sequence */ "//"[^\r\n]* /* ignore one-line comments */ "#"\ *[0-9][0-9_]* /* ignore simulation timings */ "#"\ *[0-9][0-9_]*\.[0-9][0-9_]* /* ignore simulation timings */ "#"\ *[$a-zA-Z_\.][$a-zA-Z_0-9\.]* /* ignore simulation timings */ . { return *yytext; } %% // this is a hack to avoid the 'yyinput defined but not used' error msgs void *frontend_verilog_avoid_input_warnings() { return (void*)&yyinput; } 7'>237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *  ---
 *
 *  The internal logic cell technology mapper.
 *
 *  This Verilog library contains the mapping of internal cells (e.g. $not with
 *  variable bit width) to the internal logic cells (such as the single bit $_NOT_
 *  gate). Usually this logic network is then mapped to the actual technology
 *  using e.g. the "abc" pass.
 *
 *  Note that this library does not map $mem cells. They must be mapped to logic
 *  and $dff cells using the "memory_map" pass first. (Or map it to custom cells,
 *  which is of course highly recommended for larger memories.)
 *
 */

`define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
`define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))


// --------------------------------------------------------
// Use simplemap for trivial cell types
// --------------------------------------------------------

(* techmap_simplemap *)
(* techmap_celltype = "$not $and $or $xor $xnor" *)
module _90_simplemap_bool_ops;
endmodule

(* techmap_simplemap *)
(* techmap_celltype = "$reduce_and $reduce_or $reduce_xor $reduce_xnor $reduce_bool" *)
module _90_simplemap_reduce_ops;
endmodule

(* techmap_simplemap *)
(* techmap_celltype = "$logic_not $logic_and $logic_or" *)
module _90_simplemap_logic_ops;
endmodule

(* techmap_simplemap *)
(* techmap_celltype = "$eq $eqx $ne $nex" *)
module _90_simplemap_compare_ops;
endmodule

(* techmap_simplemap *)
(* techmap_celltype = "$pos $slice $concat $mux $tribuf" *)
module _90_simplemap_various;
endmodule

(* techmap_simplemap *)
(* techmap_celltype = "$sr $ff $dff $dffe $adff $dffsr $dlatch" *)
module _90_simplemap_registers;
endmodule


// --------------------------------------------------------
// Shift operators
// --------------------------------------------------------

(* techmap_celltype = "$shr $shl $sshl $sshr" *)
module _90_shift_ops_shr_shl_sshl_sshr (A, B, Y);
	parameter A_SIGNED = 0;
	parameter B_SIGNED = 0;
	parameter A_WIDTH = 1;
	parameter B_WIDTH = 1;
	parameter Y_WIDTH = 1;

	parameter _TECHMAP_CELLTYPE_ = "";
	localparam shift_left = _TECHMAP_CELLTYPE_ == "$shl" || _TECHMAP_CELLTYPE_ == "$sshl";
	localparam sign_extend = A_SIGNED && _TECHMAP_CELLTYPE_ == "$sshr";

	input [A_WIDTH-1:0] A;
	input [B_WIDTH-1:0] B;
	output [Y_WIDTH-1:0] Y;

	localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH);
	localparam BB_WIDTH = `MIN($clog2(shift_left ? Y_WIDTH : A_SIGNED ? WIDTH : A_WIDTH) + 1, B_WIDTH);

	wire [1023:0] _TECHMAP_DO_00_ = "proc;;";
	wire [1023:0] _TECHMAP_DO_01_ = "RECURSION; CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;";

	integer i;
	reg [WIDTH-1:0] buffer;
	reg overflow;

	always @* begin
		overflow = B_WIDTH > BB_WIDTH ? |B[B_WIDTH-1:BB_WIDTH] : 1'b0;
		buffer = overflow ? {WIDTH{sign_extend ? A[A_WIDTH-1] : 1'b0}} : {{WIDTH-A_WIDTH{A_SIGNED ? A[A_WIDTH-1] : 1'b0}}, A};

		for (i = 0; i < BB_WIDTH; i = i+1)
			if (B[i]) begin
				if (shift_left)
					buffer = {buffer, (2**i)'b0};
				else if (2**i < WIDTH)
					buffer = {{2**i{sign_extend ? buffer[WIDTH-1] : 1'b0}}, buffer[WIDTH-1 : 2**i]};
				else
					buffer = {WIDTH{sign_extend ? buffer[WIDTH-1] : 1'b0}};
			end
	end

	assign Y = buffer;
endmodule

(* techmap_celltype = "$shift $shiftx" *)
module _90_shift_shiftx (A, B, Y);
	parameter A_SIGNED = 0;
	parameter B_SIGNED = 0;
	parameter A_WIDTH = 1;
	parameter B_WIDTH = 1;
	parameter Y_WIDTH = 1;

	input [A_WIDTH-1:0] A;
	input [B_WIDTH-1:0] B;
	output [Y_WIDTH-1:0] Y;

	parameter _TECHMAP_CELLTYPE_ = "";
	parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
	parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;

	localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx;

	generate
`ifndef NO_LSB_FIRST_SHIFT_SHIFTX
		// If $shift/$shiftx only shifts in units of Y_WIDTH
		//   (a common pattern created by pmux2shiftx)
		//   which is checked by ensuring that all that
		//   the appropriate LSBs of B are constant zero,
		//   then we can decompose LSB first instead of
		//   MSB first
		localparam CLOG2_Y_WIDTH = $clog2(Y_WIDTH);
		if (B_WIDTH > CLOG2_Y_WIDTH+1 &&
			_TECHMAP_CONSTMSK_B_[CLOG2_Y_WIDTH-1:0] == {CLOG2_Y_WIDTH{1'b1}} &&
			_TECHMAP_CONSTVAL_B_[CLOG2_Y_WIDTH-1:0] == {CLOG2_Y_WIDTH{1'b0}}) begin
			// Halve the size of $shift/$shiftx by $mux-ing A according to
			//   the LSB of B, after discarding the zeroed bits
			localparam Y_WIDTH2 = 2**CLOG2_Y_WIDTH;
			localparam entries = (A_WIDTH+Y_WIDTH-1)/Y_WIDTH2;
			localparam len = Y_WIDTH2 * ((entries+1)/2);
			wire [len-1:0] AA;
			wire [(A_WIDTH+Y_WIDTH2+Y_WIDTH-1)-1:0] Apad = {{(Y_WIDTH2+Y_WIDTH-1){extbit}}, A};
			genvar i;
			for (i = 0; i < A_WIDTH; i=i+Y_WIDTH2*2)
				assign AA[i/2 +: Y_WIDTH2] = B[CLOG2_Y_WIDTH] ? Apad[i+Y_WIDTH2 +: Y_WIDTH2] : Apad[i +: Y_WIDTH2];
			wire [B_WIDTH-2:0] BB = {B[B_WIDTH-1:CLOG2_Y_WIDTH+1], {CLOG2_Y_WIDTH{1'b0}}};
			if (_TECHMAP_CELLTYPE_ == "$shift")
				$shift #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(len), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .Y(Y));
			else
				$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(len), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .Y(Y));
		end
		else
`endif
		begin
			localparam BB_WIDTH = `MIN($clog2(`MAX(A_WIDTH, Y_WIDTH)) + (B_SIGNED ? 2 : 1), B_WIDTH);
			localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH) + (B_SIGNED ? 2**(BB_WIDTH-1) : 0);

			wire [1023:0] _TECHMAP_DO_00_ = "proc;;";
			wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;";

			integer i;
			reg [WIDTH-1:0] buffer;
			reg overflow;

			always @* begin
				overflow = 0;
				buffer = {WIDTH{extbit}};
				buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A;

				if (B_WIDTH > BB_WIDTH) begin
					if (B_SIGNED) begin
						for (i = BB_WIDTH; i < B_WIDTH; i = i+1)
							if (B[i] != B[BB_WIDTH-1])
								overflow = 1;
					end else
						overflow = |B[B_WIDTH-1:BB_WIDTH];
					if (overflow)
						buffer = {WIDTH{extbit}};
				end

				for (i = BB_WIDTH-1; i >= 0; i = i-1)
					if (B[i]) begin
						if (B_SIGNED && i == BB_WIDTH-1)
							buffer = {buffer, {2**i{extbit}}};
						else if (2**i < WIDTH)
							buffer = {{2**i{extbit}}, buffer[WIDTH-1 : 2**i]};
						else
							buffer = {WIDTH{extbit}};
					end
			end
			assign Y = buffer;
		end
	endgenerate
endmodule


// --------------------------------------------------------
// Arithmetic operators
// --------------------------------------------------------

(* techmap_celltype = "$fa" *)
module _90_fa (A, B, C, X, Y);
	parameter WIDTH = 1;

	input [WIDTH-1:0] A, B, C;
	output [WIDTH-1:0] X, Y;

	wire [WIDTH-1:0] t1, t2, t3;

	assign t1 = A ^ B, t2 = A & B, t3 = C & t1;
	assign Y = t1 ^ C, X = t2 | t3;
endmodule

(* techmap_celltype = "$lcu" *)
module _90_lcu (P, G, CI, CO);
	parameter WIDTH = 2;

	input [WIDTH-1:0] P, G;
	input CI;

	output [WIDTH-1:0] CO;

	integer i, j;
	reg [WIDTH-1:0] p, g;

	wire [1023:0] _TECHMAP_DO_ = "proc; opt -fast";

	always @* begin
		p = P;
		g = G;

		// in almost all cases CI will be constant zero
		g[0] = g[0] | (p[0] & CI);

		// [[CITE]] Brent Kung Adder
		// R. P. Brent and H. T. Kung, "A Regular Layout for Parallel Adders",
		// IEEE Transaction on Computers, Vol. C-31, No. 3, p. 260-264, March, 1982

		// Main tree
		for (i = 1; i <= $clog2(WIDTH); i = i+1) begin
			for (j = 2**i - 1; j < WIDTH; j = j + 2**i) begin
				g[j] = g[j] | p[j] & g[j - 2**(i-1)];
				p[j] = p[j] & p[j - 2**(i-1)];
			end
		end

		// Inverse tree
		for (i = $clog2(WIDTH); i > 0; i = i-1) begin
			for (j = 2**i + 2**(i-1) - 1; j < WIDTH; j = j + 2**i) begin
				g[j] = g[j] | p[j] & g[j - 2**(i-1)];