From 2a43d1637f87e50257df19e83e2150caa8c64a51 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 27 Feb 2024 21:43:52 +0000 Subject: [PATCH] Implement Verilog case equality --- lib/nvc/verilog-body.vhd | 89 +++++++++++++++++++++++++++++++++++++++ lib/nvc/verilog.vhd | 12 ++++++ src/lexer.l | 6 ++- src/scan.c | 2 +- src/scan.h | 4 ++ src/vlog/vlog-lower.c | 85 +++++++++++++++++++++++++++++-------- src/vlog/vlog-node.h | 6 +++ src/vlog/vlog-parse.y | 41 ++++++++++++++++++ test/regress/testlist.txt | 1 + test/regress/vlog8.v | 38 +++++++++++++++++ test/run_regr.c | 26 +++++++++++- 11 files changed, 289 insertions(+), 21 deletions(-) create mode 100644 test/regress/vlog8.v diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index a320d87e..d1437c96 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -167,6 +167,18 @@ package body verilog is end case; end function; + function to_string (value : t_packed_logic) return string is + constant length : natural := value'length; + constant lookup : string(1 to 4) := "XZ01"; + variable result : string(1 to length); + alias a_value : t_packed_logic(1 to length) is value; + begin + for i in 1 to length loop + result(i) := lookup(t_logic'pos(a_value(i)) + 1); + end loop; + return result; + end function; + function resize (value : t_packed_logic; length : natural) return t_packed_logic is constant orig : natural := value'length; alias a_value : t_packed_logic(1 to orig) is value; @@ -197,6 +209,28 @@ package body verilog is end if; end function; + function "or" (l, r : t_logic) return t_logic is + begin + if l = '0' and r = '0' then + return '0'; + elsif l = 'X' or r = 'X' or l = 'Z' or r = 'Z' then + return 'X'; + else + return '1'; + end if; + end function; + + function "xor" (l, r : t_logic) return t_logic is + begin + if (l = '1' and r = '0') or (l = '0' and r = '1') then + return '1'; + elsif l = 'X' or r = 'X' or l = 'Z' or r = 'Z' then + return 'X'; + else + return '0'; + end if; + end function; + function "and" (l, r : t_packed_logic) return t_packed_logic is constant llen : natural := l'length; constant rlen : natural := r'length; @@ -247,4 +281,59 @@ package body verilog is end loop; return result; end function; + + function add_unsigned (l, r : t_packed_logic; c : t_logic) return t_packed_logic is + constant l_left : integer := l'length - 1; + alias xl : t_packed_logic(0 to l_left) is l; + alias xr : t_packed_logic(0 to l_left) is r; + variable result : t_packed_logic(0 to l_left); + variable cbit : t_logic := c; + begin + for i in 0 to l_left loop + result(i) := cbit xor xl(i) xor xr(i); + cbit := (cbit and xl(i)) or (cbit and xr(i)) or (xl(i) and xr(i)); + end loop; + return result; + end function; + + function "+" (l, r : t_packed_logic) return t_packed_logic is + constant size : natural := maximum(l'length, r'length); + variable lext : t_packed_logic(size - 1 downto 0) := resize(l, size); + variable rext : t_packed_logic(size - 1 downto 0) := resize(r, size); + begin + return add_unsigned(lext, rext, '0'); + end function; + + function "=" (l, r : t_packed_logic) return boolean is + constant lsize : natural := l'length; + constant rsize : natural := r'length; + constant minsize : natural := minimum(lsize, rsize); + alias la : t_packed_logic(1 to lsize) is l; + alias ra : t_packed_logic(1 to rsize) is r; + begin + for i in 1 to minsize loop + if la(i) /= ra(i) then + return false; + end if; + end loop; + if lsize > rsize then + for i in minsize + 1 to lsize loop + if la(i) /= '0' then + return false; + end if; + end loop; + elsif rsize > lsize then + for i in minsize + 1 to rsize loop + if ra(i) /= '0' then + return false; + end if; + end loop; + end if; + return true; + end function; + + function "/=" (l, r : t_packed_logic) return boolean is + begin + return not (l = r); + end function; end package body; diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index d8d38f17..5552f446 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -56,16 +56,28 @@ package verilog is function to_verilog (value : std_ulogic) return t_logic; function to_verilog (value : std_ulogic) return t_net_value; + function to_string (value : t_packed_logic) return string; + function resize (value : t_packed_logic; length : natural) return t_packed_logic; function resize (value : t_logic; length : natural) return t_packed_logic; function "and" (l, r : t_logic) return t_logic; function "and" (l, r : t_packed_logic) return t_packed_logic; + function "xor" (l, r : t_logic) return t_logic; + + function "or" (l, r : t_logic) return t_logic; + function "not" (x : t_logic) return t_logic; function "not" (x : t_packed_logic) return t_packed_logic; function "not" (x : t_packed_logic) return t_logic; + function "+" (l, r : t_packed_logic) return t_packed_logic; + + function "=" (l, r : t_packed_logic) return boolean; + + function "/=" (l, r : t_packed_logic) return boolean; + procedure sys_finish; -- These procedures are called with a special variadic calling convention diff --git a/src/lexer.l b/src/lexer.l index 39b2870f..48317ad3 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -1,7 +1,7 @@ /* -*- mode: c; c-basic-offset: 3 -*- */ /* - * Copyright (C) 2011-2023 Nick Gasson + * Copyright (C) 2011-2024 Nick Gasson * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -541,6 +541,10 @@ UNION ?i:union "supply1" { return tSUPPLY1; } "pulldown" { return tPULLDOWN; } "pullup" { return tPULLUP; } +"==" { return tLOGEQ; } +"!=" { return tLOGNEQ; } +"===" { return tCASEEQ; } +"!==" { return tCASENEQ; } {SYSTASK} { yylval.str = xstrdup(yytext); return tSYSTASK; } {VLOG_ID} { yylval.str = xstrdup(yytext); return tID; } diff --git a/src/scan.c b/src/scan.c index fbebce0e..5c30b259 100644 --- a/src/scan.c +++ b/src/scan.c @@ -228,7 +228,7 @@ const char *token_str(token_t tok) "&&", "within", "system task", "view", "private", "prev", "stable", "rose", "fell", "ended", "nondet", "nondetv", "union", "translate on", "translate off", "until!", "until_", "until_!", "`timescale" - "supply0", "supply1", "pulldown", "pullup", + "supply0", "supply1", "pulldown", "pullup", "===", "!==", }; if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs)) diff --git a/src/scan.h b/src/scan.h index 15c085ed..513f8796 100644 --- a/src/scan.h +++ b/src/scan.h @@ -297,5 +297,9 @@ bool is_scanned_as_psl(void); #define tSUPPLY1 401 #define tPULLDOWN 402 #define tPULLUP 403 +#define tCASEEQ 404 +#define tCASENEQ 405 +#define tLOGEQ 406 +#define tLOGNEQ 407 #endif // _SCAN_H diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 5c31641d..11d127a2 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -168,13 +168,14 @@ static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) switch (vcode_reg_kind(reg)) { case VCODE_TYPE_INT: - { + if (vtype_eq(vcode_reg_type(reg), vtype_bool())) + return reg; + else { vcode_type_t vlogic = vlog_logic_type(); vcode_reg_t one_reg = emit_const(vlogic, LOGIC_1); assert(vtype_eq(vcode_reg_type(reg), vlogic)); return emit_cmp(VCODE_CMP_EQ, reg, one_reg); } - break; default: vcode_dump(); fatal_trace("cannot convert r%d to bool", reg); @@ -291,6 +292,69 @@ static vcode_reg_t vlog_lower_unary(lower_unit_t *lu, vlog_unary_t op, return emit_fcall(func, rtype, vlogic, args, ARRAY_LEN(args)); } +static vcode_reg_t vlog_lower_binary(lower_unit_t *lu, vlog_binary_t op, + vcode_reg_t left_reg, + vcode_reg_t right_reg) +{ + LOCAL_TEXT_BUF tb = tb_new(); + tb_cat(tb, "NVC.VERILOG."); + + switch (op) { + case V_BINARY_AND: + tb_cat(tb, "\"and\"("); + break; + case V_BINARY_OR: + tb_cat(tb, "\"or\"("); + break; + case V_BINARY_CASE_EQ: + case V_BINARY_LOG_EQ: + tb_cat(tb, "\"=\"("); + break; + case V_BINARY_CASE_NEQ: + case V_BINARY_LOG_NEQ: + tb_cat(tb, "\"/=\"("); + break; + case V_BINARY_PLUS: + tb_cat(tb, "\"+\"("); + break; + case V_BINARY_MINUS: + tb_cat(tb, "\"-\"("); + break; + } + + tb_cat(tb, T_PACKED_LOGIC T_PACKED_LOGIC ")"); + + vcode_type_t rtype; + switch (op) { + case V_BINARY_CASE_EQ: + case V_BINARY_CASE_NEQ: + rtype = vtype_bool(); + tb_cat(tb, "B"); + break; + case V_BINARY_LOG_EQ: + case V_BINARY_LOG_NEQ: + rtype = vlog_logic_type(); + tb_cat(tb, T_LOGIC); + break; + default: + rtype = vlog_packed_logic_type(); + tb_cat(tb, T_PACKED_LOGIC); + break; + } + + ident_t func = ident_new(tb_get(tb)); + + vcode_reg_t context_reg = vlog_helper_package(); + + vcode_reg_t args[] = { + context_reg, + vlog_lower_wrap(lu, left_reg), + vlog_lower_wrap(lu, right_reg) + }; + + return emit_fcall(func, rtype, rtype, args, ARRAY_LEN(args)); +} + static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) { switch (vlog_kind(v)) { @@ -400,22 +464,7 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) { vcode_reg_t left_reg = vlog_lower_rvalue(lu, vlog_left(v)); vcode_reg_t right_reg = vlog_lower_rvalue(lu, vlog_right(v)); - - ident_t func = ident_new("NVC.VERILOG.\"and\"(" T_PACKED_LOGIC - T_PACKED_LOGIC ")" T_PACKED_LOGIC); - - vcode_reg_t context_reg = vlog_helper_package(); - - vcode_reg_t args[] = { - context_reg, - vlog_lower_wrap(lu, left_reg), - vlog_lower_wrap(lu, right_reg) - }; - - vcode_type_t vlogic = vlog_logic_type(); - vcode_type_t vpacked = vlog_packed_logic_type(); - - return emit_fcall(func, vpacked, vlogic, args, ARRAY_LEN(args)); + return vlog_lower_binary(lu, vlog_subkind(v), left_reg, right_reg); } case V_UNARY: { diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index 5be8ea63..44c3865f 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -88,6 +88,12 @@ typedef enum { typedef enum { V_BINARY_OR, V_BINARY_AND, + V_BINARY_PLUS, + V_BINARY_MINUS, + V_BINARY_LOG_EQ, + V_BINARY_LOG_NEQ, + V_BINARY_CASE_EQ, + V_BINARY_CASE_NEQ, } vlog_binary_t; typedef enum { diff --git a/src/vlog/vlog-parse.y b/src/vlog/vlog-parse.y index 5f9c6282..5f0f467f 100644 --- a/src/vlog/vlog-parse.y +++ b/src/vlog/vlog-parse.y @@ -181,10 +181,18 @@ static vlog_node_t make_strength(vlog_strength_t value, const loc_t *loc) %token tSUPPLY1 401 "supply1" %token tPULLDOWN 402 "pulldown" %token tPULLUP 403 "pullup" +%token tCASEEQ 404 "===" +%token tCASENEQ 405 "!==" +%token tLOGEQ 406 "==" +%token tLOGNEQ 407 "!=" + %token tEOF 0 "end of file" %left '|' %left '&' +%left tCASEEQ tCASENEQ tLOGEQ tLOGNEQ +%left '+' '-' +%left '*' '/' '%' %left '~' '!' %precedence "then" @@ -242,6 +250,7 @@ module_declaration: module_port_list_opt: '(' list_of_port_declarations ')' { $$ = $2; } | '(' list_of_ports ')' { $$ = $2; } + | '(' ')' { $$ = NULL; } | /* empty */ { $$ = NULL; } ; @@ -753,6 +762,38 @@ expression: primary vlog_set_left($$, $1); vlog_set_right($$, $3); } + | expression '+' expression + { + $$ = vlog_new(V_BINARY); + vlog_set_loc($$, &@$); + vlog_set_subkind($$, V_BINARY_PLUS); + vlog_set_left($$, $1); + vlog_set_right($$, $3); + } + | expression '-' expression + { + $$ = vlog_new(V_BINARY); + vlog_set_loc($$, &@$); + vlog_set_subkind($$, V_BINARY_MINUS); + vlog_set_left($$, $1); + vlog_set_right($$, $3); + } + | expression tCASEEQ expression + { + $$ = vlog_new(V_BINARY); + vlog_set_loc($$, &@$); + vlog_set_subkind($$, V_BINARY_CASE_EQ); + vlog_set_left($$, $1); + vlog_set_right($$, $3); + } + | expression tCASENEQ expression + { + $$ = vlog_new(V_BINARY); + vlog_set_loc($$, &@$); + vlog_set_subkind($$, V_BINARY_CASE_NEQ); + vlog_set_left($$, $1); + vlog_set_right($$, $3); + } | '~' expression { $$ = vlog_new(V_UNARY); diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 2ad118ea..df307800 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -946,3 +946,4 @@ vlog7 verilog,gold wait27 normal issue808 verilog,gold issue851 wave,2008 +vlog8 verilog diff --git a/test/regress/vlog8.v b/test/regress/vlog8.v new file mode 100644 index 00000000..1d42e704 --- /dev/null +++ b/test/regress/vlog8.v @@ -0,0 +1,38 @@ +module vlog8(); + reg [3:0] a, b; + + initial begin + a = 1; + b = 2; + #1; + a = a + b; + b = a + b; + if (a !== 3) + begin + $display("FAILED -- a: %x !== 3", a); + $finish; + end + if (b !== 5) + begin + $display("FAILED -- b: %x !== 5", b); + $finish; + end + #2; + $display("PASSED"); + end // initial begin + + initial begin + #2; + if (a !== 3) + begin + $display("FAILED -- a (signal): %x !== 3", a); + $finish; + end + if (b !== 5) + begin + $display("FAILED -- b (signal): %x !== 5", b); + $finish; + end + end + +endmodule // testbench diff --git a/test/run_regr.c b/test/run_regr.c index 9f6649d7..dd26af6a 100644 --- a/test/run_regr.c +++ b/test/run_regr.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2016-2023 Nick Gasson +// Copyright (C) 2016-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -1120,6 +1120,30 @@ static bool run_test(test_t *test) } } } + else if (test->flags & F_VERILOG) { + outf = freopen(OUTFILE, "r", outf); + assert(outf != NULL); + + bool found_passed = false, found_failed = false; + char line[256]; + while (fgets(line, sizeof(line), outf)) { + found_passed |= (strstr(line, "PASSED") != NULL); + found_failed |= (strstr(line, "FAILED") != NULL); + } + + if (found_failed) { + failed(NULL); + result = false; + } + else if (found_failed && found_passed) { + failed("printed both PASSED and FAILED"); + result = false; + } + else if (!found_failed && !found_passed) { + failed("did not print PASSED or FAILED"); + result = false; + } + } out_print: if (result) { -- 2.39.2