From aca6f5d7eb6549d2354245c18bca061453a6f0f3 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Thu, 22 Feb 2024 16:36:45 +0000 Subject: [PATCH] Verilog pullup and pulldown gate instances --- lib/nvc/verilog-body.vhd | 103 +++++++++++++++++++++++++- lib/nvc/verilog.vhd | 21 +++++- src/common.c | 5 +- src/common.h | 3 + src/elab.c | 110 ++++++++++++++++++++++------ src/lexer.l | 4 ++ src/scan.c | 3 +- src/scan.h | 6 +- src/vlog/vlog-dump.c | 47 ++++++++++++ src/vlog/vlog-lower.c | 140 ++++++++++++++++++++++++++++++++---- src/vlog/vlog-node.c | 15 +++- src/vlog/vlog-node.h | 21 ++++++ src/vlog/vlog-parse.y | 123 ++++++++++++++++++++++++++++++- src/vlog/vlog-sem.c | 16 ++++- src/vlog/vlog-trans.c | 42 +++++++++-- test/dump/vlog1.v | 2 + test/regress/gold/vlog7.txt | 2 + test/regress/testlist.txt | 1 + test/regress/vlog7.v | 16 +++++ test/test_dump.c | 2 + test/test_elab.c | 5 +- test/test_vlog.c | 60 +++++++++++++++- test/vlog/gate1.v | 7 ++ test/vlog/parse1.v | 5 ++ 24 files changed, 704 insertions(+), 55 deletions(-) create mode 100644 test/regress/gold/vlog7.txt create mode 100644 test/regress/vlog7.v create mode 100644 test/vlog/gate1.v diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index 026de730..a320d87e 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- Copyright (C) 2023 Nick Gasson +-- Copyright (C) 2023-2024 Nick Gasson -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. @@ -18,6 +18,77 @@ use work.polyfill.all; package body verilog is + subtype t_strength is natural range 0 to 7; + + function strength (value : t_net_value) return t_strength is + begin + case value is + when highz0 | highz1 => return 0; + when small0 | small1 => return 1; + when medium0 | medium1 => return 2; + when weak0 | weak1 => return 3; + when large0 | large1 => return 4; + when pull0 | pull1 => return 5; + when strong0 | strong1 => return 6; + when supply0 | supply1 | 'X' => return 7; + end case; + end function; + + function resolved (inputs : t_net_array) return t_net_value is + variable result : t_net_value; + constant count : natural := inputs'length; + alias a_inputs : t_net_array(1 to count) is inputs; + begin + if inputs'length = 0 then + return 'X'; + else + result := a_inputs(1); + for i in 2 to count loop + if strength(a_inputs(i)) > strength(result) then + result := a_inputs(i); + end if; + end loop; + return result; + end if; + end function; + + function to_logic (value : t_net_value) return t_logic is + begin + case value is + when 'X' => + return 'X'; + when supply0 | strong0 | pull0 | large0 | weak0 + | medium0 | small0 => + return '0'; + when small1 | medium1 | weak1 | large1 | pull1 + | strong1 | supply1 => + return '1'; + when highz1 | highz0 => + return 'Z'; + end case; + end function; + + function to_net_value (value : t_logic) return t_net_value is + begin + case value is + when 'X' => return 'X'; + when '0' => return strong0; + when '1' => return strong1; + when 'Z' => return highz1; + end case; + end function; + + function to_net_value (value : t_packed_logic) return t_net_array is + constant length : natural := value'length; + alias a_value : t_packed_logic(1 to length) is value; + variable result : t_net_array(1 to length); + begin + for i in 1 to length loop + result(i) := to_net_value(a_value(i)); + end loop; + return result; + end function; + function to_integer (value : t_packed_logic) return t_int64 is alias v : t_packed_logic(0 to value'length - 1) is value; variable r : t_int64 := 0; @@ -56,6 +127,24 @@ package body verilog is end case; end function; + function to_vhdl (value : t_net_value) return std_ulogic is + begin + case value is + when 'X' => + return 'U'; + when supply0 | strong0 | pull0 | large0 => + return '0'; + when weak0 | medium0 | small0 => + return 'L'; + when highz0 | highz1 => + return 'Z'; + when small1 | medium1 | weak1 => + return 'H'; + when large1 | pull1 | strong1 | supply1 => + return '1'; + end case; + end function; + function to_verilog (value : std_ulogic) return t_logic is begin case value is @@ -66,6 +155,18 @@ package body verilog is end case; end function; + function to_verilog (value : std_ulogic) return t_net_value is + begin + case value is + when '1' => return strong1; + when 'H' => return weak1; + when '0' => return strong0; + when 'L' => return weak0; + when 'Z' => return highz1; + when others => return 'X'; + end case; + 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; diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index 2a63ed89..d8d38f17 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- Copyright (C) 2023 Nick Gasson +-- Copyright (C) 2023-2024 Nick Gasson -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. @@ -29,13 +29,32 @@ package verilog is type t_packed_logic is array (natural range <>) of t_logic; + type t_net_value is ('X', supply0, strong0, pull0, large0, weak0, + medium0, small0, highz0, highz1, small1, medium1, + weak1, large1, pull1, strong1, supply1); + + type t_net_array is array (natural range <>) of t_net_value; + + function resolved (inputs : t_net_array) return t_net_value; + + subtype t_resolved_net is resolved t_net_value; + + type t_resolved_packed_net is array (natural range <>) of t_resolved_net; + + function to_logic (value : t_net_value) return t_logic; + + function to_net_value (value : t_logic) return t_net_value; + function to_net_value (value : t_packed_logic) return t_net_array; + function to_integer (value : t_packed_logic) return t_int64; function to_time (value : t_packed_logic) return delay_length; function to_vhdl (value : t_logic) return std_ulogic; + function to_vhdl (value : t_net_value) return std_ulogic; function to_verilog (value : std_ulogic) return t_logic; + function to_verilog (value : std_ulogic) return t_net_value; function resize (value : t_packed_logic; length : natural) return t_packed_logic; function resize (value : t_logic; length : natural) return t_packed_logic; diff --git a/src/common.c b/src/common.c index fdd948df..10fbab54 100644 --- a/src/common.c +++ b/src/common.c @@ -1306,7 +1306,7 @@ static tree_t cached_verilog(void) type_t verilog_type(verilog_type_t which) { - static type_t cache[VERILOG_INT64 + 1] = {}; + static type_t cache[VERILOG_RESOLVED_PACKED_NET + 1] = {}; assert(which < ARRAY_LEN(cache)); if (cache[which] == NULL) { @@ -1314,6 +1314,9 @@ type_t verilog_type(verilog_type_t which) "T_LOGIC", "T_PACKED_LOGIC", "T_INT64", + "T_NET_VALUE", + "T_RESOLVED_NET", + "T_RESOLVED_PACKED_NET", }; tree_t d = search_decls(cached_verilog(), ident_new(names[which]), 0); diff --git a/src/common.h b/src/common.h index 0c4bb680..d595f32d 100644 --- a/src/common.h +++ b/src/common.h @@ -173,6 +173,9 @@ typedef enum { VERILOG_LOGIC, VERILOG_PACKED_LOGIC, VERILOG_INT64, + VERILOG_NET_VALUE, + VERILOG_RESOLVED_NET, + VERILOG_RESOLVED_PACKED_NET, } verilog_type_t; type_t verilog_type(verilog_type_t which); diff --git a/src/elab.c b/src/elab.c index a2504613..dc88b093 100644 --- a/src/elab.c +++ b/src/elab.c @@ -32,6 +32,7 @@ #include "option.h" #include "phase.h" #include "psl/psl-phase.h" +#include "thread.h" #include "type.h" #include "vlog/vlog-node.h" #include "vlog/vlog-phase.h" @@ -42,6 +43,9 @@ #include #include +#define T_LOGIC "19NVC.VERILOG.T_LOGIC" +#define T_NET_VALUE "23NVC.VERILOG.T_NET_VALUE" + typedef A(tree_t) tree_list_t; typedef struct _elab_ctx elab_ctx_t; @@ -1055,6 +1059,67 @@ static void elab_lower(tree_t b, vcode_unit_t shape, elab_ctx_t *ctx) diag_remove_hint_fn(elab_hint_fn); } +static tree_t elab_to_vhdl(type_t from, type_t to) +{ + static struct { + const verilog_type_t from_id; + const ieee_type_t to_id; + const char *const func; + type_t from; + type_t to; + tree_t decl; + } table[] = { + { VERILOG_LOGIC, IEEE_STD_LOGIC, "NVC.VERILOG.TO_VHDL(" T_LOGIC ")U" }, + { VERILOG_NET_VALUE, IEEE_STD_LOGIC, + "NVC.VERILOG.TO_VHDL(" T_NET_VALUE ")U" }, + }; + + INIT_ONCE({ + for (int i = 0; i < ARRAY_LEN(table); i++) { + table[i].from = verilog_type(table[i].from_id); + table[i].to = ieee_type(table[i].to_id); + table[i].decl = verilog_func(ident_new(table[i].func)); + } + }); + + for (int i = 0; i < ARRAY_LEN(table); i++) { + if (type_eq(table[i].from, from) && type_eq(table[i].to, to)) + return table[i].decl; + } + + return NULL; +} + +static tree_t elab_to_verilog(type_t from, type_t to) +{ + static struct { + const ieee_type_t from_id; + const verilog_type_t to_id; + const char *const func; + type_t from; + type_t to; + tree_t decl; + } table[] = { + { IEEE_STD_ULOGIC, VERILOG_LOGIC, "NVC.VERILOG.TO_VERILOG(U)" T_LOGIC }, + { IEEE_STD_ULOGIC, VERILOG_NET_VALUE, + "NVC.VERILOG.TO_VERILOG(U)" T_NET_VALUE } + }; + + INIT_ONCE({ + for (int i = 0; i < ARRAY_LEN(table); i++) { + table[i].from = ieee_type(table[i].from_id); + table[i].to = verilog_type(table[i].to_id); + table[i].decl = verilog_func(ident_new(table[i].func)); + } + }); + + for (int i = 0; i < ARRAY_LEN(table); i++) { + if (type_eq(table[i].from, from) && type_eq(table[i].to, to)) + return table[i].decl; + } + + return NULL; +} static void elab_mixed_port_map(tree_t block, vlog_node_t mod, const elab_ctx_t *ctx) { @@ -1064,15 +1129,6 @@ static void elab_mixed_port_map(tree_t block, vlog_node_t mod, bit_mask_t have; mask_init(&have, nports); - type_t std_logic = ieee_type(IEEE_STD_LOGIC); - -#define T_LOGIC "19NVC.VERILOG.T_LOGIC" - ident_t to_vhdl_name = ident_new("NVC.VERILOG.TO_VHDL(" T_LOGIC ")U"); - ident_t to_verilog_name = ident_new("NVC.VERILOG.TO_VERILOG(U)" T_LOGIC); - - tree_t to_vhdl = verilog_func(to_vhdl_name); - tree_t to_verilog = verilog_func(to_verilog_name); - bool have_named = false; for (int i = 0; i < ndecls; i++) { vlog_node_t mport = vlog_decl(mod, i); @@ -1116,19 +1172,23 @@ static void elab_mixed_port_map(tree_t block, vlog_node_t mod, return; } - type_t type = tree_type(bport); - if (!type_eq(type, std_logic)) { - error_at(tree_loc(bport), "Verilog module ports must have " - "type STD_LOGIC or STD_LOGIC_VECTOR"); - return; - } + type_t btype = tree_type(bport); + type_t vtype = tree_type(vport); if (vlog_subkind(mport) == V_PORT_INPUT) { + tree_t func = elab_to_verilog(btype, vtype); + if (func == NULL) { + error_at(tree_loc(bport), "cannot connect VHDL signal with type " + "%s to Verilog input port %s", type_pp(btype), + istr(vlog_ident(mport))); + return; + } + tree_t conv = tree_new(T_CONV_FUNC); tree_set_loc(conv, tree_loc(bport)); - tree_set_ref(conv, to_verilog); - tree_set_ident(conv, tree_ident(to_verilog)); - tree_set_type(conv, type_result(tree_type(to_verilog))); + tree_set_ref(conv, func); + tree_set_ident(conv, tree_ident(func)); + tree_set_type(conv, type_result(tree_type(func))); tree_set_value(conv, make_ref(bport)); if (have_named) @@ -1137,11 +1197,19 @@ static void elab_mixed_port_map(tree_t block, vlog_node_t mod, add_param(ctx->out, conv, P_POS, NULL); } else { + tree_t func = elab_to_vhdl(vtype, btype); + if (func == NULL) { + error_at(tree_loc(bport), "cannot connect VHDL signal with type " + "%s to Verilog output port %s", type_pp(btype), + istr(vlog_ident(mport))); + return; + } + tree_t conv = tree_new(T_CONV_FUNC); tree_set_loc(conv, tree_loc(bport)); - tree_set_ref(conv, to_vhdl); - tree_set_ident(conv, tree_ident(to_vhdl)); - tree_set_type(conv, type_result(tree_type(to_vhdl))); + tree_set_ref(conv, func); + tree_set_ident(conv, tree_ident(func)); + tree_set_type(conv, type_result(tree_type(func))); tree_set_value(conv, make_ref(vport)); add_param(ctx->out, make_ref(bport), P_NAMED, conv); diff --git a/src/lexer.l b/src/lexer.l index fddfc755..39b2870f 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -537,6 +537,10 @@ UNION ?i:union "if" { return tIF; } "else" { return tELSE; } "`timescale" { return tTIMESCALE; } +"supply0" { return tSUPPLY0; } +"supply1" { return tSUPPLY1; } +"pulldown" { return tPULLDOWN; } +"pullup" { return tPULLUP; } {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 f4cc2e10..fbebce0e 100644 --- a/src/scan.c +++ b/src/scan.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2023 Nick Gasson +// Copyright (C) 2014-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 @@ -228,6 +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", }; if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs)) diff --git a/src/scan.h b/src/scan.h index aa941b55..15c085ed 100644 --- a/src/scan.h +++ b/src/scan.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-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 @@ -293,5 +293,9 @@ bool is_scanned_as_psl(void); #define tUNTIL_ 397 #define tUNTIL_1 398 #define tTIMESCALE 399 +#define tSUPPLY0 400 +#define tSUPPLY1 401 +#define tPULLDOWN 402 +#define tPULLUP 403 #endif // _SCAN_H diff --git a/src/vlog/vlog-dump.c b/src/vlog/vlog-dump.c index be7af4ed..56a92c42 100644 --- a/src/vlog/vlog-dump.c +++ b/src/vlog/vlog-dump.c @@ -282,6 +282,47 @@ static void vlog_dump_delay_control(vlog_node_t v, int indent) print_syntax(" "); } +static void vlog_dump_gate_inst(vlog_node_t v, int indent) +{ + tab(indent); + + switch (vlog_subkind(v)) { + case V_GATE_PULLUP: print_syntax("#pullup "); break; + case V_GATE_PULLDOWN: print_syntax("#pulldown "); break; + } + + const int nparams = vlog_params(v); + if (nparams > 0) { + print_syntax("("); + for (int i = 0; i < nparams; i++) { + if (i > 0) print_syntax(","); + vlog_dump(vlog_param(v, i), 0); + } + print_syntax(") "); + } + + if (vlog_has_ident(v)) + print_syntax("%s ", istr(vlog_ident(v))); + + print_syntax("("); + vlog_dump(vlog_target(v), 0); + print_syntax(");\n"); +} + +static void vlog_dump_strength(vlog_node_t v, int indent) +{ + switch (vlog_subkind(v)) { + case V_STRENGTH_PULL0: print_syntax("#pull0"); break; + case V_STRENGTH_STRONG0: print_syntax("#strong0"); break; + case V_STRENGTH_WEAK0: print_syntax("#weak0"); break; + case V_STRENGTH_SUPPLY0: print_syntax("#supply0"); break; + case V_STRENGTH_PULL1: print_syntax("#pull1"); break; + case V_STRENGTH_STRONG1: print_syntax("#strong1"); break; + case V_STRENGTH_WEAK1: print_syntax("#weak1"); break; + case V_STRENGTH_SUPPLY1: print_syntax("#supply1"); break; + } +} + void vlog_dump(vlog_node_t v, int indent) { switch (vlog_kind(v)) { @@ -342,6 +383,12 @@ void vlog_dump(vlog_node_t v, int indent) case V_DELAY_CONTROL: vlog_dump_delay_control(v, indent); break; + case V_GATE_INST: + vlog_dump_gate_inst(v, indent); + break; + case V_STRENGTH: + vlog_dump_strength(v, indent); + break; default: print_syntax("\n"); fflush(stdout); diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 713e515d..a40558d6 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -42,6 +42,14 @@ #define T_LOGIC "19NVC.VERILOG.T_LOGIC" #define T_PACKED_LOGIC "26NVC.VERILOG.T_PACKED_LOGIC" #define T_INT64 "19NVC.VERILOG.T_INT64" +#define T_NET_VALUE "23NVC.VERILOG.T_NET_VALUE" +#define T_NET_ARRAY "23NVC.VERILOG.T_NET_ARRAY" + +typedef enum { + _X = 0, _SUPPLY0, _STRONG0, _PULL0, _LARGE0, _WEAK0, _MEDIUM0, + _SMALL0, _HIGHZ0, _HIGHZ1, _SMALL1, _MEDIUM1, _WEAK1, _LARGE1, + _PULL1, _STRONG1, _SUPPLY1 +} net_value_t; static void vlog_lower_stmts(lower_unit_t *lu, vlog_node_t v); static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v); @@ -51,6 +59,11 @@ static inline vcode_type_t vlog_logic_type(void) return vtype_int(0, 3); } +static inline vcode_type_t vlog_net_value_type(void) +{ + return vtype_int(0, 16); +} + static inline vcode_type_t vlog_packed_logic_type(void) { vcode_type_t vlogic = vlog_logic_type(); @@ -68,6 +81,20 @@ static vcode_reg_t vlog_debug_locus(vlog_node_t v) } #endif +static bool vlog_is_net(vlog_node_t v) +{ + switch (vlog_kind(v)) { + case V_NET_DECL: + return true; + case V_PORT_DECL: + return vlog_subkind(v) != V_PORT_OUTPUT_REG; + case V_REF: + return vlog_is_net(vlog_ref(v)); + default: + return false; + } +} + static vcode_reg_t vlog_helper_package(void) { return emit_link_package(ident_new("NVC.VERILOG")); @@ -76,18 +103,18 @@ static vcode_reg_t vlog_helper_package(void) static vcode_reg_t vlog_lower_wrap(lower_unit_t *lu, vcode_reg_t reg) { vcode_type_t voffset = vtype_offset(); + vcode_type_t regtype = vcode_reg_type(reg); vcode_reg_t left_reg = emit_const(voffset, 0), right_reg, data_reg; - switch (vcode_reg_kind(reg)) { + switch (vtype_kind(regtype)) { case VCODE_TYPE_CARRAY: data_reg = emit_address_of(reg); - right_reg = emit_const(voffset, vtype_size(vcode_reg_type(reg)) - 1); + right_reg = emit_const(voffset, vtype_size(regtype) - 1); break; case VCODE_TYPE_INT: { - vcode_type_t vlogic = vlog_logic_type(); ident_t name = ident_uniq("wrap_temp"); - vcode_var_t tmp = emit_var(vlogic, vlogic, name, VAR_TEMP); + vcode_var_t tmp = emit_var(regtype, regtype, name, VAR_TEMP); emit_store(reg, tmp); data_reg = emit_index(tmp, VCODE_INVALID_REG); @@ -302,6 +329,7 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) else nets_reg = emit_load_indirect(emit_var_upref(hops, var)); + vcode_reg_t resolved_reg; if (vcode_reg_kind(nets_reg) == VCODE_TYPE_UARRAY) { vcode_reg_t data_reg = emit_resolved(emit_unwrap(nets_reg)); @@ -311,15 +339,26 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) .right = emit_uarray_right(nets_reg, 0), .dir = emit_uarray_dir(nets_reg, 0) }, }; - return emit_wrap(data_reg, dims, 1); + resolved_reg = emit_wrap(data_reg, dims, 1); } else { - vcode_reg_t resolved_reg = emit_resolved(nets_reg); + vcode_reg_t data_reg = emit_resolved(nets_reg); if (vlog_ranges(decl) > 0) - return vlog_lower_decl_bounds(lu, decl, resolved_reg); + resolved_reg = vlog_lower_decl_bounds(lu, decl, data_reg); else - return emit_load_indirect(resolved_reg); + resolved_reg = emit_load_indirect(data_reg); + } + + if (vlog_is_net(decl)) { + vcode_reg_t context_reg = vlog_helper_package(); + vcode_reg_t args[] = { context_reg, resolved_reg }; + vcode_type_t vlogic = vlog_logic_type(); + ident_t func = ident_new("NVC.VERILOG.TO_LOGIC(" + T_NET_VALUE ")" T_LOGIC); + return emit_fcall(func, vlogic, vlogic, args, ARRAY_LEN(args)); } + else + return resolved_reg; } case V_EVENT: { @@ -402,6 +441,15 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) vcode_reg_t value_reg = vlog_lower_rvalue(lu, vlog_value(v)); return vlog_lower_unary(lu, vlog_subkind(v), value_reg); } + case V_STRENGTH: + { + static const int8_t map[] = { + _SUPPLY0, _STRONG0, _PULL0, _WEAK0, + _SUPPLY1, _STRONG1, _PULL1, _WEAK1 + }; + vcode_type_t vnet = vlog_net_value_type(); + return emit_const(vnet, map[vlog_subkind(v)]); + } default: CANNOT_HANDLE(v); } @@ -474,7 +522,9 @@ static void vlog_lower_timing(lower_unit_t *lu, vlog_node_t v, bool is_static) static void vlog_lower_nbassign(lower_unit_t *lu, vlog_node_t v) { - vcode_reg_t target_reg = vlog_lower_lvalue(lu, vlog_target(v)); + vlog_node_t target = vlog_target(v); + + vcode_reg_t target_reg = vlog_lower_lvalue(lu, target); vcode_reg_t value_reg = vlog_lower_rvalue(lu, vlog_value(v)); vcode_reg_t count_reg, nets_reg; @@ -488,7 +538,21 @@ static void vlog_lower_nbassign(lower_unit_t *lu, vlog_node_t v) } vcode_reg_t resize_reg = vlog_lower_resize(lu, value_reg, count_reg); - vcode_reg_t data_reg = emit_unwrap(resize_reg); + + vcode_reg_t data_reg; + if (vlog_is_net(target)) { + vcode_reg_t context_reg = vlog_helper_package(); + vcode_reg_t args[] = { context_reg, resize_reg }; + vcode_type_t vnet = vlog_net_value_type(); + vcode_type_t varray = vtype_uarray(1, vnet, vnet); + ident_t func = ident_new("NVC.VERILOG.TO_NET_VALUE(" + T_PACKED_LOGIC ")" T_NET_ARRAY); + vcode_reg_t call_reg = emit_fcall(func, varray, vnet, + args, ARRAY_LEN(args)); + data_reg = emit_unwrap(call_reg); + } + else + data_reg = emit_unwrap(resize_reg); vcode_type_t vtime = vtype_time(); vcode_reg_t reject_reg = emit_const(vtime, 0); @@ -751,6 +815,49 @@ static void vlog_lower_continuous_assign(unit_registry_t *ur, unit_registry_finalise(ur, lu); } +static void vlog_lower_gate_inst(unit_registry_t *ur, lower_unit_t *parent, + vlog_node_t stmt) +{ + vcode_unit_t context = get_vcode(parent); + + ident_t name = ident_prefix(vcode_unit_name(context), vlog_ident(stmt), '.'); + vcode_unit_t vu = emit_process(name, vlog_to_object(stmt), context); + + vcode_block_t start_bb = emit_block(); + assert(start_bb == 1); + + lower_unit_t *lu = lower_unit_new(ur, parent, vu, NULL, NULL); + unit_registry_put(ur, lu); + + vlog_lower_driver(lu, vlog_target(stmt)); + + emit_return(VCODE_INVALID_REG); + + vcode_select_block(start_bb); + + vcode_reg_t value_reg = 0; + if (vlog_params(stmt) == 0) { + const vlog_gate_kind_t kind = vlog_subkind(stmt); + vcode_type_t vnet = vlog_net_value_type(); + value_reg = emit_const(vnet, kind == V_GATE_PULLUP ? _PULL1 : _PULL0); + } + else + value_reg = vlog_lower_rvalue(lu, vlog_param(stmt, 0)); + + vcode_type_t vtime = vtype_time(); + vcode_reg_t reject_reg = emit_const(vtime, 0); + vcode_reg_t after_reg = emit_const(vtime, 0); + + vcode_reg_t nets_reg = vlog_lower_lvalue(lu, vlog_target(stmt)); + vcode_reg_t count_reg = emit_const(vtype_offset(), 1); + + emit_sched_waveform(nets_reg, count_reg, value_reg, reject_reg, after_reg); + + emit_wait(start_bb, VCODE_INVALID_REG); + + unit_registry_finalise(ur, lu); +} + static void vlog_lower_concurrent(unit_registry_t *ur, lower_unit_t *parent, vlog_node_t scope) { @@ -767,6 +874,9 @@ static void vlog_lower_concurrent(unit_registry_t *ur, lower_unit_t *parent, case V_ASSIGN: vlog_lower_continuous_assign(ur, parent, s); break; + case V_GATE_INST: + vlog_lower_gate_inst(ur, parent, s); + break; default: CANNOT_HANDLE(s); } @@ -788,18 +898,20 @@ vcode_unit_t vlog_lower(unit_registry_t *ur, tree_t wrap) unit_registry_put(ur, lu); vcode_type_t vlogic = vlog_logic_type(); - vcode_type_t vsignal = vtype_signal(vlogic); + vcode_type_t vnetvalue = vlog_net_value_type(); + vcode_type_t vlogicsignal = vtype_signal(vlogic); + vcode_type_t vnetsignal = vtype_signal(vnetvalue); const int ndecls = vlog_decls(mod); for (int i = 0; i < ndecls; i++) { vlog_node_t d = vlog_decl(mod, i); switch (vlog_kind(d)) { case V_PORT_DECL: - case V_VAR_DECL: case V_NET_DECL: + case V_VAR_DECL: { - vcode_var_t var = - emit_var(vsignal, vsignal, vlog_ident(d), VAR_SIGNAL); + vcode_type_t vtype = vlog_is_net(d) ? vnetsignal : vlogicsignal; + vcode_var_t var = emit_var(vtype, vtype, vlog_ident(d), VAR_SIGNAL); lower_put_vcode_obj(d, var, lu); } break; diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index e2ea6cf5..20f96624 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022 Nick Gasson +// Copyright (C) 2022-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 @@ -91,6 +91,12 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { // V_UNARY (I_VALUE | I_SUBKIND), + + // V_GATE_INST + (I_SUBKIND | I_PARAMS | I_IDENT | I_REF | I_TARGET), + + // V_STRENGTH + (I_SUBKIND), }; static const char *kind_text_map[V_LAST_NODE_KIND] = { @@ -99,7 +105,7 @@ static const char *kind_text_map[V_LAST_NODE_KIND] = { "V_SEQ_BLOCK", "V_SYSTASK", "V_STRING", "V_NUMBER", "V_NET_DECL", "V_ASSIGN", "V_DIMENSION", "V_IF", "V_COND", "V_VAR_DECL", "V_DELAY_CONTROL", "V_BINARY", - "V_BASSIGN", "V_UNARY", + "V_BASSIGN", "V_UNARY", "V_GATE_INST", "V_STRENGTH", }; static const change_allowed_t change_allowed[] = { @@ -161,6 +167,11 @@ void vlog_set_ident(vlog_node_t v, ident_t i) lookup_item(&vlog_object, v, I_IDENT)->ident = i; } +bool vlog_has_ident(vlog_node_t v) +{ + return lookup_item(&vlog_object, v, I_IDENT)->ident != NULL; +} + ident_t vlog_ident2(vlog_node_t v) { item_t *item = lookup_item(&vlog_object, v, I_IDENT2); diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index f1556c8f..53d58e3f 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -41,6 +41,8 @@ typedef enum { typedef enum { V_NET_WIRE, + V_NET_SUPPLY0, + V_NET_SUPPLY1, } vlog_net_kind_t; typedef enum { @@ -66,6 +68,8 @@ typedef enum { V_BINARY, V_BASSIGN, V_UNARY, + V_GATE_INST, + V_STRENGTH, V_LAST_NODE_KIND } vlog_kind_t; @@ -95,6 +99,22 @@ typedef enum { V_ASSIGN_EQUALS, } vlog_assign_t; +typedef enum { + V_GATE_PULLDOWN, + V_GATE_PULLUP, +} vlog_gate_kind_t; + +typedef enum { + V_STRENGTH_SUPPLY0, + V_STRENGTH_STRONG0, + V_STRENGTH_PULL0, + V_STRENGTH_WEAK0, + V_STRENGTH_SUPPLY1, + V_STRENGTH_STRONG1, + V_STRENGTH_PULL1, + V_STRENGTH_WEAK1, +} vlog_strength_t; + vlog_node_t vlog_new(vlog_kind_t kind); vlog_kind_t vlog_kind(vlog_node_t v); const char *vlog_kind_str(vlog_kind_t kind); @@ -106,6 +126,7 @@ void vlog_set_loc(vlog_node_t v, const loc_t *loc); ident_t vlog_ident(vlog_node_t v); void vlog_set_ident(vlog_node_t v, ident_t i); +bool vlog_has_ident(vlog_node_t v); ident_t vlog_ident2(vlog_node_t v); void vlog_set_ident2(vlog_node_t v, ident_t i); diff --git a/src/vlog/vlog-parse.y b/src/vlog/vlog-parse.y index 32712c7f..d1c8b6d9 100644 --- a/src/vlog/vlog-parse.y +++ b/src/vlog/vlog-parse.y @@ -1,6 +1,6 @@ // -*- mode: bison; c-basic-offset: 3 -*- // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-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 @@ -107,6 +107,15 @@ static bool is_decl(vlog_node_t v) return false; } } + +static vlog_node_t make_strength(vlog_strength_t value, const loc_t *loc) +{ + vlog_node_t s = vlog_new(V_STRENGTH); + vlog_set_loc(s, loc); + vlog_set_subkind(s, value); + return s; +} + %} %type module_declaration primary expression @@ -118,7 +127,8 @@ static bool is_decl(vlog_node_t v) %type port initial_construct net_assignment %type seq_block system_task_enable string number %type decimal_number conditional_statement variable_type -%type delay_control delay_value +%type delay_control delay_value strength0 strength1 +%type pull_gate_instance %type identifier hierarchical_identifier %type module_item_list module_port_list_opt module_item %type list_of_port_declarations module_item_list_opt @@ -128,6 +138,7 @@ static bool is_decl(vlog_node_t v) %type module_or_generate_item continuous_assign %type list_of_net_assignments reg_declaration %type list_of_variable_identifiers list_of_statements_opt +%type gate_instantiation pulldown_strength pullup_strength %type external_identifier %type net_type @@ -153,6 +164,10 @@ static bool is_decl(vlog_node_t v) %token tIF 234 "if" %token tELSE 255 "else" %token tTIMESCALE 399 "`timescale" +%token tSUPPLY0 400 "supply0" +%token tSUPPLY1 401 "supply1" +%token tPULLDOWN 402 "pulldown" +%token tPULLUP 403 "pullup" %token tEOF 0 "end of file" %left '|' @@ -319,6 +334,7 @@ module_or_generate_item: | always_construct { $$ = node_list_single($1); } | initial_construct { $$ = node_list_single($1); } | continuous_assign + | gate_instantiation ; module_or_generate_item_declaration: @@ -350,6 +366,8 @@ net_declaration: ; net_type: tWIRE { $$ = V_NET_WIRE; } + | tSUPPLY0 { $$ = V_NET_SUPPLY0; } + | tSUPPLY1 { $$ = V_NET_SUPPLY1; } ; list_of_net_identifiers: @@ -758,6 +776,107 @@ external_identifier: } ; +gate_instantiation: + tPULLDOWN pulldown_strength pull_gate_instance ';' + { + vlog_set_subkind($3, V_GATE_PULLDOWN); + + for (node_list_t *it = $2; it; it = it->next) + vlog_add_param($3, it->value); + node_list_free($2); + + $$ = NULL; + node_list_append(&$$, $3); + } + | tPULLDOWN pull_gate_instance ';' + { + vlog_set_subkind($2, V_GATE_PULLDOWN); + + $$ = NULL; + node_list_append(&$$, $2); + } + | tPULLUP pullup_strength pull_gate_instance ';' + { + vlog_set_subkind($3, V_GATE_PULLUP); + + for (node_list_t *it = $2; it; it = it->next) + vlog_add_param($3, it->value); + node_list_free($2); + + $$ = NULL; + node_list_append(&$$, $3); + } + | tPULLUP pull_gate_instance ';' + { + vlog_set_subkind($2, V_GATE_PULLUP); + + $$ = NULL; + node_list_append(&$$, $2); + } + ; + +pull_gate_instance: + identifier '(' lvalue ')' + { + $$ = vlog_new(V_GATE_INST); + vlog_set_loc($$, &@$); + vlog_set_ident($$, $1); + vlog_set_target($$, $3); + } + | '(' lvalue ')' + { + $$ = vlog_new(V_GATE_INST); + vlog_set_loc($$, &@$); + vlog_set_target($$, $2); + } + ; + +pulldown_strength: + '(' strength0 ',' strength1 ')' + { + $$ = NULL; + node_list_append(&$$, $2); + node_list_append(&$$, $4); + } + | '(' strength1 ',' strength0 ')' + { + $$ = NULL; + node_list_append(&$$, $2); + node_list_append(&$$, $4); + } + | '(' strength0 ')' + { + $$ = NULL; + node_list_append(&$$, $2); + } + ; + +pullup_strength: + '(' strength0 ',' strength1 ')' + { + $$ = NULL; + node_list_append(&$$, $2); + node_list_append(&$$, $4); + } + | '(' strength1 ',' strength0 ')' + { + $$ = NULL; + node_list_append(&$$, $2); + node_list_append(&$$, $4); + } + | '(' strength1 ')' + { + $$ = NULL; + node_list_append(&$$, $2); + } + ; + +strength0: tSUPPLY0 { $$ = make_strength(V_STRENGTH_SUPPLY0, &@$); } + ; + +strength1: tSUPPLY1 { $$ = make_strength(V_STRENGTH_SUPPLY1, &@$); } + ; + %% static void yyerror(const char *s) diff --git a/src/vlog/vlog-sem.c b/src/vlog/vlog-sem.c index 59bcf2a7..5d6d4da5 100644 --- a/src/vlog/vlog-sem.c +++ b/src/vlog/vlog-sem.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-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 @@ -301,6 +301,17 @@ static void vlog_check_module(vlog_node_t module) pop_scope(); } +static void vlog_check_gate_inst(vlog_node_t g) +{ + vlog_node_t target = vlog_target(g); + vlog_check(target); + + if (vlog_has_ident(g)) + vlog_insert_decl(g); + else + vlog_set_ident(g, ident_uniq("#gate")); +} + void vlog_check(vlog_node_t v) { switch (vlog_kind(v)) { @@ -364,6 +375,9 @@ void vlog_check(vlog_node_t v) case V_UNARY: vlog_check_unary(v); break; + case V_GATE_INST: + vlog_check_gate_inst(v); + break; default: fatal_at(vlog_loc(v), "cannot check verilog node %s", vlog_kind_str(vlog_kind(v))); diff --git a/src/vlog/vlog-trans.c b/src/vlog/vlog-trans.c index d8a7728a..79e6812f 100644 --- a/src/vlog/vlog-trans.c +++ b/src/vlog/vlog-trans.c @@ -48,11 +48,12 @@ static tree_t trans_expr(vlog_node_t expr) } } -static type_t trans_type(vlog_node_t decl) +static type_t trans_type(vlog_node_t decl, verilog_type_t scalar_type, + verilog_type_t packed_type) { const int nranges = vlog_ranges(decl); if (nranges > 0) { - type_t packed = verilog_type(VERILOG_PACKED_LOGIC); + type_t packed = verilog_type(packed_type); tree_t c = tree_new(T_CONSTRAINT); tree_set_subkind(c, C_INDEX); @@ -85,7 +86,17 @@ static type_t trans_type(vlog_node_t decl) return sub; } else - return verilog_type(VERILOG_LOGIC); + return verilog_type(scalar_type); +} + +static type_t trans_var_type(vlog_node_t decl) +{ + return trans_type(decl, VERILOG_LOGIC, VERILOG_PACKED_LOGIC); +} + +static type_t trans_net_type(vlog_node_t decl) +{ + return trans_type(decl, VERILOG_RESOLVED_NET, VERILOG_RESOLVED_PACKED_NET); } static void trans_port_decl(vlog_node_t decl, tree_t out) @@ -97,11 +108,17 @@ static void trans_port_decl(vlog_node_t decl, tree_t out) [V_PORT_INOUT] = PORT_INOUT, }; + const v_port_kind_t kind = vlog_subkind(decl); + tree_t t = tree_new(T_PORT_DECL); tree_set_ident(t, vlog_ident(decl)); - tree_set_subkind(t, map[vlog_subkind(decl)]); + tree_set_subkind(t, map[kind]); tree_set_class(t, C_SIGNAL); - tree_set_type(t, trans_type(decl)); + + if (kind == V_PORT_OUTPUT_REG) + tree_set_type(t, trans_var_type(decl)); + else + tree_set_type(t, trans_net_type(decl)); tree_add_port(out, t); } @@ -110,7 +127,16 @@ static void trans_var_decl(vlog_node_t decl, tree_t out) { tree_t t = tree_new(T_SIGNAL_DECL); tree_set_ident(t, vlog_ident(decl)); - tree_set_type(t, trans_type(decl)); + tree_set_type(t, trans_var_type(decl)); + + tree_add_decl(out, t); +} + +static void trans_net_decl(vlog_node_t decl, tree_t out) +{ + tree_t t = tree_new(T_SIGNAL_DECL); + tree_set_ident(t, vlog_ident(decl)); + tree_set_type(t, trans_net_type(decl)); tree_add_decl(out, t); } @@ -125,9 +151,11 @@ void vlog_trans(vlog_node_t mod, tree_t out) trans_port_decl(d, out); break; case V_VAR_DECL: - case V_NET_DECL: trans_var_decl(d, out); break; + case V_NET_DECL: + trans_net_decl(d, out); + break; default: CANNOT_HANDLE(d); } diff --git a/test/dump/vlog1.v b/test/dump/vlog1.v index ac15a0f8..dc80ebe6 100644 --- a/test/dump/vlog1.v +++ b/test/dump/vlog1.v @@ -6,6 +6,7 @@ endmodule // dff module mod2; wire [7:0] bus; + wire w; reg r; initial begin $display("hello"); @@ -16,4 +17,5 @@ module mod2; #1 r <= 0; end assign bus = 3; + pullup (supply1, supply0) p1 (w); endmodule // mod2 diff --git a/test/regress/gold/vlog7.txt b/test/regress/gold/vlog7.txt new file mode 100644 index 00000000..8edbf763 --- /dev/null +++ b/test/regress/gold/vlog7.txt @@ -0,0 +1,2 @@ +x x x +0 1 0 diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 9645dd49..c0fb4bbc 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -942,3 +942,4 @@ ename7 normal,2008 issue844 normal,2008 conv14 normal,2008 issue850 shell +vlog7 verilog,gold diff --git a/test/regress/vlog7.v b/test/regress/vlog7.v new file mode 100644 index 00000000..631de149 --- /dev/null +++ b/test/regress/vlog7.v @@ -0,0 +1,16 @@ +module vlog7; + wire w1, w2, w3; + pullup (w1); + pullup (supply1) (w2); + pulldown (w3); + + assign w1 = 0; + assign w2 = 0; + + initial begin + $display("%x %x %x", w1, w2, w3); /// XXX: wrong + #0; + $display("%x %x %x", w1, w2, w3); + $finish; + end +endmodule // vlog7 diff --git a/test/test_dump.c b/test/test_dump.c index 2b51e145..397f0a3d 100644 --- a/test/test_dump.c +++ b/test/test_dump.c @@ -474,6 +474,7 @@ START_TEST(test_vlog1) diff_dump(tb_get(tb), "module mod2;\n" " wire [5'b111:5'b0] bus;\n" + " wire w;\n" " reg r;\n" " initial begin\n" " $display(\"hello\");\n" @@ -484,6 +485,7 @@ START_TEST(test_vlog1) " #5'b1 r <= 5'b0;\n" " end\n" " assign bus = 5'b11;\n" + " pullup (supply1,supply0) p1 (w);\n" "endmodule // mod2\n\n"); tb_rewind(tb); diff --git a/test/test_elab.c b/test/test_elab.c index e469d4d1..944bcae6 100644 --- a/test/test_elab.c +++ b/test/test_elab.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2011-2022 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 @@ -1501,7 +1501,8 @@ START_TEST(test_mixed1) { 22, "missing matching VHDL port declaration for Verilog port two " "in component MOD1" }, { 34, "port FOUR not found in Verilog module mod1" }, - { 44, "Verilog module ports must have type STD_LOGIC or STD_LOGIC_VEC" }, + { 44, "cannot connect VHDL signal with type BIT to Verilog output " + "port three" }, { -1, NULL } }; expect_errors(expect); diff --git a/test/test_vlog.c b/test/test_vlog.c index a9f8bf05..e9e7ed05 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -157,7 +157,7 @@ START_TEST(test_parse1) vlog_node_t m = vlog_parse(); fail_if(m == NULL); fail_unless(vlog_kind(m) == V_MODULE); - fail_unless(vlog_stmts(m) == 3); + fail_unless(vlog_stmts(m) == 8); fail_unless(vlog_ports(m) == 0); fail_unless(vlog_decls(m) == 4); @@ -213,6 +213,41 @@ START_TEST(test_parse1) vlog_node_t s1s0s5 = vlog_stmt(s1s0, 5); fail_unless(vlog_kind(s1s0s5) == V_BASSIGN); + vlog_node_t s3 = vlog_stmt(m, 3); + fail_unless(vlog_kind(s3) == V_GATE_INST); + ck_assert_int_eq(vlog_subkind(s3), V_GATE_PULLDOWN); + ck_assert_int_eq(vlog_params(s3), 1); + fail_unless(vlog_ident(s3) == ident_new("p1")); + fail_unless(vlog_kind(vlog_target(s3)) == V_REF); + + vlog_node_t s4 = vlog_stmt(m, 4); + fail_unless(vlog_kind(s4) == V_GATE_INST); + ck_assert_int_eq(vlog_subkind(s4), V_GATE_PULLDOWN); + ck_assert_int_eq(vlog_params(s4), 0); + fail_unless(vlog_ident(s4) == ident_new("p2")); + fail_unless(vlog_kind(vlog_target(s4)) == V_REF); + + vlog_node_t s5 = vlog_stmt(m, 5); + fail_unless(vlog_kind(s5) == V_GATE_INST); + ck_assert_int_eq(vlog_subkind(s5), V_GATE_PULLUP); + ck_assert_int_eq(vlog_params(s5), 0); + fail_unless(vlog_ident(s5) == ident_new("p3")); + fail_unless(vlog_kind(vlog_target(s5)) == V_REF); + + vlog_node_t s6 = vlog_stmt(m, 6); + fail_unless(vlog_kind(s6) == V_GATE_INST); + ck_assert_int_eq(vlog_subkind(s6), V_GATE_PULLUP); + ck_assert_int_eq(vlog_params(s6), 2); + fail_unless(vlog_ident(s6) == ident_new("p4")); + fail_unless(vlog_kind(vlog_target(s6)) == V_REF); + + vlog_node_t s7 = vlog_stmt(m, 7); + fail_unless(vlog_kind(s7) == V_GATE_INST); + ck_assert_int_eq(vlog_subkind(s7), V_GATE_PULLUP); + ck_assert_int_eq(vlog_params(s7), 0); + fail_if(vlog_has_ident(s7)); + fail_unless(vlog_kind(vlog_target(s7)) == V_REF); + fail_unless(vlog_parse() == NULL); fail_if_errors(); @@ -321,6 +356,28 @@ START_TEST(test_timescale1) } END_TEST +START_TEST(test_gate1) +{ + input_from_file(TESTDIR "/vlog/gate1.v"); + + const error_t expect[] = { + { 6, "duplicate declaration of p1" }, + { -1, NULL } + }; + expect_errors(expect); + + vlog_node_t m = vlog_parse(); + fail_if(m == NULL); + fail_unless(vlog_kind(m) == V_MODULE); + + vlog_check(m); + + fail_unless(vlog_parse() == NULL); + + check_expected_errors(); +} +END_TEST + Suite *get_vlog_tests(void) { Suite *s = suite_create("vlog"); @@ -335,6 +392,7 @@ Suite *get_vlog_tests(void) tcase_add_test(tc, test_pp1); tcase_add_test(tc, test_empty1); tcase_add_test(tc, test_timescale1); + tcase_add_test(tc, test_gate1); suite_add_tcase(s, tc); return s; diff --git a/test/vlog/gate1.v b/test/vlog/gate1.v new file mode 100644 index 00000000..3ca59cdc --- /dev/null +++ b/test/vlog/gate1.v @@ -0,0 +1,7 @@ +module gate1; + wire w; + reg r; + pullup (w); // OK + pulldown (supply0) p1 (r); // OK + pullup (supply1) p1 (w); // Error +endmodule // gate1 diff --git a/test/vlog/parse1.v b/test/vlog/parse1.v index dd21dcff..fc6cce02 100644 --- a/test/vlog/parse1.v +++ b/test/vlog/parse1.v @@ -15,4 +15,9 @@ module parse1; z = !x; end assign x = x | y; + pulldown (supply0) p1 (x); + pulldown p2 (x); + pullup p3 (y); + pullup (supply0, supply1) p4 (y); + pullup (y); endmodule // parse1 -- 2.39.2