diff --git a/README.md b/README.md index 35143ef..5fb4b02 100644 --- a/README.md +++ b/README.md @@ -114,10 +114,6 @@ larger expression must take the form `smaller + offset` or `offset + smaller`. Otherwise the output will be a direct transcription of the VHDL, which is not standard-conforming unless both ends of the range are constant. -Conversion functions (resize, to_unsigned, conv_integer) are parsed, but -their semantics are ignored: resize(foo,n), to_unsigned(foo,n), and -conv_integer(foo) are treated as equivalent to foo. - VHDL is case insensitive, vhd2vl is case retentive, and Verilog is case sensitive. If you're sloppy with case in the original VHDL, the resulting Verilog will have compile-time warnings or errors. See diff --git a/examples/Makefile b/examples/Makefile index e7d4f2c..7c8859d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -6,8 +6,8 @@ all: ifneq ($(shell which ghdl),) @mkdir -p $(TEMP) @echo "##### Checking examples with GHDL ##############################" - ghdl -a --workdir=$(TEMP) signextend.vhd - ghdl -a -fsynopsys --workdir=$(TEMP) *.vhd + ghdl -a --std=08 --workdir=$(TEMP) signextend.vhd + ghdl -a --std=08 -fsynopsys --workdir=$(TEMP) *.vhd endif clean: diff --git a/examples/agg_risk_demo.vhd b/examples/agg_risk_demo.vhd index 1db2b3d..35ebc02 100644 --- a/examples/agg_risk_demo.vhd +++ b/examples/agg_risk_demo.vhd @@ -3,22 +3,19 @@ use ieee.std_logic_1164.all; entity agg_risk is port( + a : in std_logic; + b : in std_logic; y : out std_logic ); end entity agg_risk; architecture rtl of agg_risk is - constant a_c : std_logic_vector(0 downto 0) := "0"; - constant b_c : std_logic_vector(0 downto 0) := "1"; - signal y_sel : std_logic; begin -- Concatenation selector uses op='c' and must be wrapped in Verilog. - -- If not wrapped, this becomes "case(a_c,b_c)". - with a_c & b_c select - y_sel <= '0' when "00", - '1' when "01", - '0' when "10", - '1' when others; - - y <= y_sel; + -- If not wrapped, this becomes "case(a,b)". + with a & b select + y <= '0' when "00", + '1' when "01", + '0' when "10", + '1' when others; end architecture rtl; diff --git a/examples/conv_integer.vhd b/examples/conv_integer.vhd index d30ed4b..0474961 100644 --- a/examples/conv_integer.vhd +++ b/examples/conv_integer.vhd @@ -4,8 +4,12 @@ use ieee.std_logic_arith.all; entity conv_integer_sink is port( - val_u : in integer; - val_i : in integer + val_u : in integer; + val_i : in integer; + val_u4 : in integer; + val_i4 : in integer; + val_u_port : in integer; + val_i_port : in integer ); end entity; @@ -27,18 +31,24 @@ entity conv_integer_demo is end entity; architecture rtl of conv_integer_demo is - constant s_test_neg : std_logic_vector(7 downto 0) := x"FF"; + constant s_test_neg : std_logic_vector(7 downto 0) := x"FF"; + signal sig_4 : std_logic_vector(3 downto 0) := "1111"; begin -- conv_integer should be parsed as CONVFUNC_1 and preserved as expr u <= conv_integer(unsigned(s)+1); i <= conv_integer(signed(s)); - -- conv_integer in port map, using constant for static expression + -- conv_integer in port map: constant/signal/port, signed/unsigned, various widths sink_inst : entity work.conv_integer_sink port map( - -- expected 255 - val_u => conv_integer(unsigned(s_test_neg)), - -- expected -1 - val_i => conv_integer(signed(s_test_neg)) + -- constant (param_list): 8-bit + val_u => conv_integer(unsigned(s_test_neg)), -- expected 255 + val_i => conv_integer(signed(s_test_neg)), -- expected -1 + -- signal (sig_list): 4-bit + val_u4 => conv_integer(unsigned(sig_4)), -- expected 15 + val_i4 => conv_integer(signed(sig_4)), -- expected -1 + -- port (io_list): 8-bit + val_u_port => conv_integer(unsigned(s)), + val_i_port => conv_integer(signed(s)) ); end architecture; \ No newline at end of file diff --git a/examples/resize.vhd b/examples/resize.vhd new file mode 100644 index 0000000..21ff648 --- /dev/null +++ b/examples/resize.vhd @@ -0,0 +1,17 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity resize_demo is + port( + u4 : in unsigned(3 downto 0); + y8 : out unsigned(7 downto 0); + y2 : out unsigned(1 downto 0) + ); +end resize_demo; + +architecture rtl of resize_demo is +begin + y8 <= resize(u4, 8); -- expect extension to 8 bits + y2 <= resize(u4, 2); -- expect truncation to 2 bits +end rtl; \ No newline at end of file diff --git a/src/vhd2vl.l b/src/vhd2vl.l index 281e6a8..38888d0 100644 --- a/src/vhd2vl.l +++ b/src/vhd2vl.l @@ -143,7 +143,7 @@ static void replace_dash(char *s); "to_signed" | "to_unsigned" | "conv_std_logic_vector" | -"conv_std_ulogic_vector" { return CONVFUNC_2; } +"conv_std_ulogic_vector" { yylval.txt=xstrdup(yytext); return CONVFUNC_2; } "to_integer" | "conv_integer" { yylval.txt=xstrdup(yytext); return CONVFUNC_1; } diff --git a/src/vhd2vl.y b/src/vhd2vl.y index 0c04ee7..7ee56e5 100644 --- a/src/vhd2vl.y +++ b/src/vhd2vl.y @@ -73,6 +73,7 @@ int timescale_emitted=0; /* Track if timescale has been output */ sglist *io_list=NULL; sglist *sig_list=NULL; sglist *type_list=NULL; +sglist *param_list=NULL; /* consts and params for width lookup */ blknamelist *blkname_list=NULL; /* need a stack of clock-edges because all edges are processed before all processes are processed. @@ -92,6 +93,46 @@ int convfunc2_is_port=0; int convfunc1_sgn=0; slist *slwith; +/* For port map: component being instantiated and formal port name (for width lookup) */ +typedef struct { char *compnt; char *formal; } portmap_formal_t; +static portmap_formal_t portmap_ctx = { NULL, NULL }; + +typedef struct entity_ports { + char *name; + sglist *ports; + struct entity_ports *next; +} entity_ports_t; +static entity_ports_t *entity_ports_list = NULL; + +sglist *lookup(sglist *sg, const char *s); + +/* Port map: lookup formal port width from entity. */ +static int lookup_formal_width(const char *compnt, const char *formal) { + entity_ports_t *ep; + sglist *sg; + vrange *r; + + ep = entity_ports_list; + while (ep && strcmp(ep->name, compnt) != 0) ep = ep->next; + if (!ep) { + return 0; + } + + sg = lookup(ep->ports, formal); + if (!sg || !sg->range) { + return 0; + } + + r = sg->range; + if (r->sizeval > 0) { + return r->sizeval; + } + if (r->vtype == tSCALAR && r->nhi && r->nlo) { + return 32; /* FORMAL INTEGER */ + } + return 0; +} + /* Indentation variables */ int indent=0; slist *indents[MAXINDENT]; @@ -611,7 +652,7 @@ slist *build_compare(expdata*left,const char *op,expdata*right) return sl; } -sglist *lookup(sglist *sg,char *s){ +sglist *lookup(sglist *sg, const char *s){ for(;;){ if(sg == NULL || strcmp(sg->name,s)==0) return sg; @@ -619,6 +660,125 @@ sglist *lookup(sglist *sg,char *s){ } } +/* Get source width for resize(vec, w) from symbol table. Returns 0 if unknown. */ +static sglist *get_src_sglist(expdata *e){ + sglist *sg; + if (!e || e->op != 't' || !e->sl || e->sl->type != tTXT || e->sl->slst != NULL) { + return NULL; + } + sg = lookup(io_list, e->sl->data.txt); + sg = sg ? sg : lookup(sig_list, e->sl->data.txt); + sg = sg ? sg : lookup(param_list, e->sl->data.txt); + return sg; +} + +static int get_src_width(expdata *e){ + sglist *sg = get_src_sglist(e); + return (sg && sg->range && sg->range->sizeval > 0) ? sg->range->sizeval : 0; +} + +/* Port map: $signed(inner)/$unsigned(inner) -> {{ext{sign_or_zero}}, expr} when formal needs wider. */ +static slist *build_conv_portmap_ext(expdata *e, int tgt) { + slist *sl; + slist *node; + slist *inner; + sglist *sg; + int src, ext; + + if (!e || tgt <= 0 || (e->op != 'S' && e->op != 'U') || !e->sl) { + return addsl(NULL, expr_to_sl(e)); + } + + for (node = e->sl; node; node = node->slst) { + if (node->type == tSLIST) + break; + } + if (!node || !node->data.sl || node->data.sl->type != tTXT || node->data.sl->slst) { + return addsl(NULL, expr_to_sl(e)); + } + inner = node->data.sl; + + /* Resolve inner id in port/signal/param scope to get bit width */ + sg = lookup(io_list, inner->data.txt); + sg = sg ? sg : lookup(sig_list, inner->data.txt); + sg = sg ? sg : lookup(param_list, inner->data.txt); + src = (sg && sg->range && sg->range->sizeval > 0) ? sg->range->sizeval : 0; + if (src <= 0 || tgt <= src) { + return addsl(NULL, expr_to_sl(e)); + } + + ext = tgt - src; + sl = addtxt(NULL, "{{"); + sl = addval(sl, ext); + sl = addtxt(sl, "{"); + if (e->op == 'S') { + sl = addsl(sl, inner); + sl = addtxt(sl, "["); + /* Use declared MSB (nhi) for sign bit; src-1 assumes 0-based [w-1:0] */ + if (sg && sg->range && sg->range->nhi) { + sl = addsl(sl, sg->range->nhi); + } else { + sl = addval(sl, src - 1); + } + sl = addtxt(sl, "]}},"); + } else { + sl = addtxt(sl, "1'b0}},"); + } + sl = addsl(sl, expr_to_sl(e)); + sl = addtxt(sl, "}"); + return sl; +} + +static slist *build_resize(expdata *vec, int width_val, int src_width){ + slist *sl; + sglist *sg; + vrange *r; + if (width_val > src_width) { + /* KNOWN LIMITATION: always zero-extends. VHDL resize() on a signed vector + * should sign-extend (replicate MSB), but sglist does not yet track the + * signed/unsigned type of each signal, so we cannot distinguish here. */ + sl = addtxt(NULL, "{{("); + sl = addval(sl, width_val - src_width); + sl = addtxt(sl, "){1'b0}},"); + sl = addsl(sl, expr_to_sl(vec)); + sl = addtxt(sl, "}"); + } else if (width_val < src_width) { + /* [width_val-1:0] assumes 0-based vec; wrong for e.g. [15:8]. Use nlo when known. */ + sg = get_src_sglist(vec); + r = (sg && sg->range) ? sg->range : NULL; + if (r && r->nlo) { + slist *n = r->nlo; + int nlo_zero = (!n->slst && ((n->type == tVAL && n->data.val == 0) || + (n->type == tTXT && n->data.txt && strcmp(n->data.txt, "0") == 0))); + sl = addsl(NULL, expr_to_sl(vec)); + sl = addtxt(sl, "["); + if (nlo_zero) { + sl = addval(sl, width_val - 1); + sl = addtxt(sl, ":0]"); + } else { + sl = addtxt(sl, "("); + sl = addsl(sl, r->nlo); + sl = addtxt(sl, ")+"); + sl = addval(sl, width_val - 1); + sl = addtxt(sl, ":"); + sl = addsl(sl, r->nlo); + sl = addtxt(sl, "]"); + } + } else { + /* nlo unknown; assume 0-based and output [w-1:0]. May be wrong for non-0-based vectors. */ + fprintf(stderr, "WARNING (line %d): resize() truncation: nlo unknown, assuming 0-based; output [%d:0] may be incorrect\n", + lineno, width_val - 1); + sl = addsl(NULL, expr_to_sl(vec)); + sl = addtxt(sl, "["); + sl = addval(sl, width_val - 1); + sl = addtxt(sl, ":0]"); + } + } else { + sl = addsl(NULL, expr_to_sl(vec)); + } + return sl; +} + char *sbottom(slist *sl){ while(sl->slst != NULL) sl=sl->slst; @@ -1232,6 +1392,13 @@ entity : ENTITY NAME IS rem PORT '(' rem portlist ')' ';' rem END opt_entity } sl=addtxt(sl,"\n"); sl=addsl(sl,$11); /* rem2 */ + /* Preserve entity ports for port map formal width lookup */ + { entity_ports_t *ep=xmalloc(sizeof(entity_ports_t)); + ep->name=xstrdup($2); + ep->ports=io_list; + ep->next=entity_ports_list; + entity_ports_list=ep; + } $$=addtxt(sl,"\n"); } /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 */ @@ -1261,6 +1428,13 @@ entity : ENTITY NAME IS rem PORT '(' rem portlist ')' ';' rem END opt_entity } sl=addtxt(sl,"\n"); sl=addsl(sl,$19); /* rem2 */ + /* Preserve entity ports for port map formal width lookup */ + { entity_ports_t *ep=xmalloc(sizeof(entity_ports_t)); + ep->name=xstrdup($2); + ep->ports=io_list; + ep->next=entity_ports_list; + entity_ports_list=ep; + } $$=addtxt(sl,"\n"); } ; @@ -1610,12 +1784,20 @@ a_decl : {$$=NULL;} $$=addrem(sl,$10); } | a_decl CONSTANT NAME ':' type ':' '=' expr ';' rem { - slist * sl; + slist *sl; + sglist *p; sl=addtxt($1,"parameter "); sl=addtxt(sl,$3); sl=addtxt(sl," = "); sl=addsl(sl,$8->sl); sl=addtxt(sl,";"); + p=xmalloc(sizeof(sglist)); + p->name=$3; + p->range=$5; + p->type=NULL; + p->dir=NULL; + p->next=param_list; + param_list=p; $$=addrem(sl,$10); } | a_decl TYPE NAME IS '(' s_list ')' ';' rem { @@ -1776,19 +1958,20 @@ a_body : rem {$$=addind($1);} $$=addsl(sl,$11); } /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ - | rem NAME ':' NAME rem PORT MAP '(' doindent map_list rem ')' ';' unindent a_body { + | rem NAME ':' NAME rem PORT MAP '(' doindent { portmap_ctx.compnt=$4; portmap_ctx.formal=NULL; } map_list rem ')' ';' unindent a_body { slist *sl; sl=addsl($1,indents[indent]); sl=addtxt(sl,$4); /* NAME2 */ sl=addtxt(sl," "); sl=addtxt(sl,$2); /* NAME1 */ sl=addtxt(sl,"(\n"); - sl=addsl(sl,$10); /* map_list */ + sl=addsl(sl,$11); /* map_list */ sl=addtxt(sl,");\n\n"); - $$=addsl(sl,$15); /* a_body */ + portmap_ctx.compnt=NULL; + $$=addsl(sl,$16); /* a_body */ } /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 */ - | rem NAME ':' NAME rem GENERIC MAP '(' doindent generic_map_list ')' rem unindent PORT MAP '(' doindent map_list ')' ';' unindent a_body { + | rem NAME ':' NAME rem GENERIC MAP '(' doindent generic_map_list ')' rem unindent PORT MAP '(' doindent { portmap_ctx.compnt=$4; portmap_ctx.formal=NULL; } map_list ')' ';' unindent a_body { slist *sl; sl=addsl($1,indents[indent]); sl=addtxt(sl,$4); /* NAME2 (component name) */ @@ -1802,9 +1985,10 @@ a_body : rem {$$=addind($1);} sl=addsl(sl,indents[indent]); sl=addtxt(sl,$2); /* NAME1 (instance name) */ sl=addtxt(sl,"(\n"); - sl=addsl(sl,$18); /* map_list */ + sl=addsl(sl,$19); /* map_list */ sl=addtxt(sl,");\n\n"); - $$=addsl(sl,$22); /* a_body */ + portmap_ctx.compnt=NULL; + $$=addsl(sl,$23); /* a_body */ } | optname PROCESS '(' sign_list ')' p_decl opt_is BEGN doindent p_body END PROCESS oname ';' unindent a_body { slist *sl; @@ -2371,22 +2555,22 @@ map_list : rem map_item { slist *sl; sl=addsl($1,indents[indent]); $$=addsl(sl,$2);} - | rem map_item ',' map_list { + | rem map_item ',' { portmap_ctx.formal=NULL; } map_list { slist *sl; sl=addsl($1,indents[indent]); sl=addsl(sl,$2); sl=addtxt(sl,",\n"); - $$=addsl(sl,$4); + $$=addsl(sl,$5); } ; map_item : mvalue {$$=$1;} - | NAME '=' '>' mvalue { + | NAME '=' '>' { portmap_ctx.formal=$1; } mvalue { slist *sl; sl=addtxt(NULL,"."); sl=addtxt(sl,$1); sl=addtxt(sl,"("); - sl=addsl(sl,$4); + sl=addsl(sl,$5); $$=addtxt(sl,")"); } ; @@ -2410,8 +2594,12 @@ mvalue : STRING {$$=addvec(NULL,$1);} } | CONVFUNC_1 '(' {convfunc1_sgn++;} expr ')' { /* Type conversion function in port map */ + int tgt = 0; convfunc1_sgn--; - $$=addsl(NULL,expr_to_sl($4)); + if (portmap_ctx.compnt && portmap_ctx.formal) { + tgt = lookup_formal_width(portmap_ctx.compnt, portmap_ctx.formal); + } + $$ = (tgt > 0) ? build_conv_portmap_ext($4, tgt) : addsl(NULL, expr_to_sl($4)); free($1); } | CONVFUNC_2 '(' expr ',' expr ')' { @@ -2420,7 +2608,22 @@ mvalue : STRING {$$=addvec(NULL,$1);} int literal_val = 0; slist *lit; if (!(expdata_literal_int($3, &literal_val))) { - $$=addsl(NULL,expr_to_sl($3)); + int is_resize = ($1 && strcmp($1, "resize") == 0); + int has_width = expdata_literal_int($5, &width_val) && width_val > 0; + + if (is_resize && has_width) { + int src_width = get_src_width($3); + if (src_width > 0) { + $$ = build_resize($3, width_val, src_width); + } else { + $$ = addsl(NULL, expr_to_sl($3)); + } + free($3); + free($5); + } else { + $$ = addsl(NULL, expr_to_sl($3)); + free($5); + } } else if (expdata_literal_int($5, &width_val)) { if (width_val > 0) { if (literal_val == 0) { @@ -2444,6 +2647,7 @@ mvalue : STRING {$$=addvec(NULL,$1);} } else { $$=addsl(NULL,expr_to_sl($3)); } + free($1); } ; @@ -2741,13 +2945,31 @@ expr : signal { free($1); } | CONVFUNC_2 '(' expr ',' expr ')' { - /* two argument type conversion e.g. to_signed(x, 3) */ + /* two argument type conversion: to_signed(x,3), to_unsigned(x,3), resize(vec,w) */ int width_val = 0; int literal_val = 0; if (!(expdata_literal_int($3, &literal_val))) { - $$ = addnest($3); - free($5); + /* $3 is vector: resize(vec,w) or pass-through */ + int is_resize = ($1 && strcmp($1, "resize") == 0); + int has_width = expdata_literal_int($5, &width_val) && width_val > 0; + if (is_resize && has_width) { + int src_width = get_src_width($3); + if (src_width > 0) { + expdata *e=xmalloc(sizeof(expdata)); + e->op='t'; + e->sl=build_resize($3, width_val, src_width); + free($3); + free($5); + $$=e; + } else { + $$ = addnest($3); + free($5); + } + } else { + $$ = addnest($3); + free($5); + } } else if (expdata_literal_int($5, &width_val)) { if (literal_val < 0) { if (width_val > 0) { @@ -2811,6 +3033,7 @@ expr : signal { $$ = addnest($3); free($5); } + free($1); } | '(' expr ')' { $$ = addnest($2); diff --git a/translated_examples/agg_risk_demo.v b/translated_examples/agg_risk_demo.v index f827298..8295009 100644 --- a/translated_examples/agg_risk_demo.v +++ b/translated_examples/agg_risk_demo.v @@ -1,27 +1,25 @@ // no timescale needed module agg_risk( -output wire y +input wire a, +input wire b, +output reg y ); -parameter a_c = 1'b0; -parameter b_c = 1'b1; -reg y_sel; // Concatenation selector uses op='c' and must be wrapped in Verilog. - // If not wrapped, this becomes "case(a_c,b_c)". + // If not wrapped, this becomes "case(a,b)". always @(*) begin - case({a_c,b_c}) - 2'b00 : y_sel <= 1'b0; - 2'b01 : y_sel <= 1'b1; - 2'b10 : y_sel <= 1'b0; - default : y_sel <= 1'b1; + case({a,b}) + 2'b00 : y <= 1'b0; + 2'b01 : y <= 1'b1; + 2'b10 : y <= 1'b0; + default : y <= 1'b1; endcase end - assign y = y_sel; endmodule diff --git a/translated_examples/conv_integer.v b/translated_examples/conv_integer.v index ce627c6..3cdda71 100644 --- a/translated_examples/conv_integer.v +++ b/translated_examples/conv_integer.v @@ -2,7 +2,11 @@ module conv_integer_sink( input wire [31:0] val_u, -input wire [31:0] val_i +input wire [31:0] val_i, +input wire [31:0] val_u4, +input wire [31:0] val_i4, +input wire [31:0] val_u_port, +input wire [31:0] val_i_port ); @@ -23,16 +27,26 @@ output wire [31:0] i parameter s_test_neg = 8'hFF; +wire [3:0] sig_4 = 4'b1111; // conv_integer should be parsed as CONVFUNC_1 and preserved as expr assign u = $unsigned(s) + 1; assign i = $signed(s); - // conv_integer in port map, using constant for static expression + // conv_integer in port map: constant/signal/port, signed/unsigned, various widths conv_integer_sink sink_inst( + // constant (param_list): 8-bit + .val_u({{24{1'b0}},$unsigned(s_test_neg)}), // expected 255 - .val_u($unsigned(s_test_neg)), + .val_i({{24{s_test_neg[7]}},$signed(s_test_neg)}), // expected -1 - .val_i($signed(s_test_neg))); + // signal (sig_list): 4-bit + .val_u4({{28{1'b0}},$unsigned(sig_4)}), + // expected 15 + .val_i4({{28{sig_4[3]}},$signed(sig_4)}), + // expected -1 + // port (io_list): 8-bit + .val_u_port({{24{1'b0}},$unsigned(s)}), + .val_i_port({{24{s[7]}},$signed(s)})); endmodule diff --git a/translated_examples/resize.v b/translated_examples/resize.v new file mode 100644 index 0000000..0cab78f --- /dev/null +++ b/translated_examples/resize.v @@ -0,0 +1,18 @@ +// no timescale needed + +module resize_demo( +input wire [3:0] u4, +output wire [7:0] y8, +output wire [1:0] y2 +); + + + + + + assign y8 = {{(4){1'b0}},u4}; + // expect extension to 8 bits + assign y2 = u4[1:0]; + // expect truncation to 2 bits + +endmodule