Skip to content

Commit 59c3e29

Browse files
committed
Added VHDL type-correctness fixes and structural validation tests
- Fixed arithmetic expressions to wrap results in std_logic_vector() - Fixed numeric literals to emit as to_unsigned/to_signed instead of bare c_N identifiers - Fixed bitwise ops and unary NOT to return std_logic_vector - Added per-entity library/use preamble for multi-entity files - Added 5 structural VHDL validation tests (balanced constructs, declared signals, type wrapping) - Added ci_validate.c exercising the full supported C subset - Upgraded actions/checkout@v4 to v5 (Node 20 deprecation fix)
1 parent b2e98d0 commit 59c3e29

File tree

7 files changed

+364
-29
lines changed

7 files changed

+364
-29
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313

1414
steps:
1515
- name: Checkout
16-
uses: actions/checkout@v4
16+
uses: actions/checkout@v5
1717

1818
- name: Install dependencies
1919
run: |

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)
44
![Language: C11](https://img.shields.io/badge/Language-C11-blue.svg)
55
![Build: CMake](https://img.shields.io/badge/Build-CMake_3.14+-orange.svg)
6-
![Tests: 75 passing](https://img.shields.io/badge/Tests-75_passing-brightgreen.svg)
6+
![Tests: 81 passing](https://img.shields.io/badge/Tests-81_passing-brightgreen.svg)
77
![cppcheck: clean](https://img.shields.io/badge/cppcheck-clean-brightgreen.svg)
88

99
Minimal C subset → VHDL translator
@@ -57,7 +57,7 @@ begin
5757
if reset = '1' then
5858
sum <= (others => '0');
5959
elsif rising_edge(clk) then
60-
sum <= a + b;
60+
sum <= std_logic_vector(unsigned(a) + unsigned(b));
6161
result <= sum;
6262
end if;
6363
end process;
@@ -73,7 +73,7 @@ Each C function becomes a VHDL entity with clock/reset ports, input parameters a
7373
- Self-contained functions with return value propagation, plus structs and arrays
7474
- VHDL entity/architecture generation with clock/reset, signals, and synchronous processes
7575
- Multi-level error diagnostics (error/warning/note across 5 categories)
76-
- 76 unit, integration, and edge case tests (GoogleTest)
76+
- 81 unit, integration, structural validation, and edge case tests (GoogleTest)
7777

7878
## Requirements
7979

@@ -115,7 +115,7 @@ examples/ — Sample C input files
115115
- No pointer support
116116
- No `switch/case` or `do-while`
117117
- Function calls are parsed, but cross-function hardware wiring is not yet synthesized. Multi-function files emit independent entities, so keep hardware-generating examples self-contained.
118-
- Generated VHDL targets the currently supported C subset, but automated simulator verification is not yet part of the test/CI flow. The output is also unoptimized — no resource sharing, no pipelining, no constant folding (see [ROADMAP.md](ROADMAP.md) Phase 4 for planned optimizations)
118+
- Generated VHDL targets the currently supported C subset. Structural well-formedness is verified by self-contained validation tests (balanced constructs, declared signals, proper type wrapping). The output is unoptimized — no resource sharing, no pipelining, no constant folding (see [ROADMAP.md](ROADMAP.md) Phase 4 for planned optimizations)
119119

120120
## Documentation
121121

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Features
3030
- Self-contained functions with return value propagation, plus structs with field access and arrays with indexing
3131
- VHDL entity/architecture generation with clock/reset, signals, and synchronous processes
3232
- Multi-level error diagnostics with colored output (error/warning/note across 5 categories)
33-
- 76 unit, integration, and edge case tests (GoogleTest)
33+
- 81 unit, integration, structural validation, and edge case tests (GoogleTest)
3434

3535
See :doc:`examples` for C-to-VHDL translation samples and :doc:`modules` for the
3636
complete source code reference.

examples/ci_validate.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// CI validation input — each function here must produce analyzable VHDL.
2+
// This file exercises the core supported C subset: arithmetic, conditionals,
3+
// loops (while, for, nested), bitwise ops, break/continue, and return values.
4+
5+
int add(int a, int b) {
6+
int sum = a + b;
7+
return sum;
8+
}
9+
10+
int bitwise_ops(int x, int y) {
11+
int a = x & y;
12+
int o = x | y;
13+
int xr = x ^ y;
14+
return a + o + xr;
15+
}
16+
17+
int negate(int x) {
18+
return -x;
19+
}
20+
21+
int max_val(int a, int b) {
22+
if (a > b) {
23+
return a;
24+
} else {
25+
return b;
26+
}
27+
}
28+
29+
int while_sum(int n) {
30+
int sum = 0;
31+
int i = 0;
32+
while (i < n) {
33+
sum = sum + i;
34+
i = i + 1;
35+
}
36+
return sum;
37+
}
38+
39+
int nested_loops(int outer, int inner) {
40+
int total = 0;
41+
int i = 0;
42+
while (i < outer) {
43+
int j = 0;
44+
while (j < inner) {
45+
total = total + i + j;
46+
j = j + 1;
47+
}
48+
i = i + 1;
49+
}
50+
return total;
51+
}
52+
53+
int for_loop(int n) {
54+
int s = 0;
55+
for (int i = 0; i < n; i++) {
56+
s = s + i;
57+
}
58+
return s;
59+
}
60+
61+
int break_continue(int n) {
62+
int total = 0;
63+
int i = 0;
64+
while (i < n) {
65+
if (i == 3) {
66+
i = i + 1;
67+
continue;
68+
}
69+
if (total > 100) {
70+
break;
71+
}
72+
total = total + i;
73+
i = i + 1;
74+
}
75+
return total;
76+
}
77+
78+
int comparison_return(int a, int b) {
79+
return a == b;
80+
}

src/codegen/codegen_vhdl_expressions.c

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,61 +63,63 @@ void generate_binary_expression(ASTNode *node, void (*node_generator)(ASTNode*))
6363
return;
6464
}
6565

66-
// Bitwise operations
66+
// Bitwise operations — result is unsigned, wrap in std_logic_vector
6767
if (strcmp(op, OP_BITWISE_AND) == 0)
6868
{
69-
emit_raw("unsigned(");
69+
emit_raw("std_logic_vector(unsigned(");
7070
node_generator(left_operand);
7171
emit_raw(") and unsigned(");
7272
node_generator(right_operand);
73-
emit_raw(")");
73+
emit_raw("))");
7474
return;
7575
}
7676

7777
if (strcmp(op, OP_BITWISE_OR) == 0)
7878
{
79-
emit_raw("unsigned(");
79+
emit_raw("std_logic_vector(unsigned(");
8080
node_generator(left_operand);
8181
emit_raw(") or unsigned(");
8282
node_generator(right_operand);
83-
emit_raw(")");
83+
emit_raw("))");
8484
return;
8585
}
8686

8787
if (strcmp(op, OP_BITWISE_XOR) == 0)
8888
{
89-
emit_raw("unsigned(");
89+
emit_raw("std_logic_vector(unsigned(");
9090
node_generator(left_operand);
9191
emit_raw(") xor unsigned(");
9292
node_generator(right_operand);
93-
emit_raw(")");
93+
emit_raw("))");
9494
return;
9595
}
9696

9797
if (strcmp(op, OP_SHIFT_LEFT) == 0)
9898
{
99-
emit_raw("shift_left(unsigned(");
99+
emit_raw("std_logic_vector(shift_left(unsigned(");
100100
node_generator(left_operand);
101101
emit_raw("), to_integer(unsigned(");
102102
node_generator(right_operand);
103-
emit_raw("))))");
103+
emit_raw(")))))");
104104
return;
105105
}
106106

107107
if (strcmp(op, OP_SHIFT_RIGHT) == 0)
108108
{
109-
emit_raw("shift_right(unsigned(");
109+
emit_raw("std_logic_vector(shift_right(unsigned(");
110110
node_generator(left_operand);
111111
emit_raw("), to_integer(unsigned(");
112112
node_generator(right_operand);
113-
emit_raw("))))");
113+
emit_raw(")))))");
114114
return;
115115
}
116116

117-
// Fallback: arithmetic or unknown operators
118-
node_generator(left_operand);
117+
// Fallback: arithmetic — use typed operands and wrap in std_logic_vector
118+
emit_raw("std_logic_vector(");
119+
emit_typed_operand(left_operand, 0, node_generator);
119120
emit_raw(" %s ", op);
120-
node_generator(right_operand);
121+
emit_typed_operand(right_operand, 0, node_generator);
122+
emit_raw(")");
121123
}
122124

123125
// Map C identifiers, array accesses, struct fields, and literals to VHDL signals.
@@ -141,15 +143,26 @@ void generate_expression(ASTNode *node)
141143
{
142144
if (isalpha(node->value[1]) || node->value[1] == '_')
143145
{
144-
emit_raw("-unsigned(%s)", node->value + 1);
146+
emit_raw("std_logic_vector(0 - unsigned(%s))", node->value + 1);
145147
}
146148
else
147149
{
150+
emit_raw("std_logic_vector(");
148151
emit_signed_cast(node->value);
152+
emit_raw(")");
149153
}
150154
return;
151155
}
152156

157+
// Positive numeric literals: emit as std_logic_vector(to_unsigned(N, BIT_WIDTH))
158+
if (is_numeric_literal(node->value))
159+
{
160+
emit_raw("std_logic_vector(");
161+
emit_unsigned_cast(node->value);
162+
emit_raw(")");
163+
return;
164+
}
165+
153166
// Struct field encoded as a__b -> a.b
154167
if (strstr(node->value, "__") != NULL)
155168
{
@@ -204,9 +217,9 @@ void generate_unary_operation(ASTNode *node, void (*node_generator)(ASTNode*))
204217
}
205218
else if (strcmp(node->value, OP_BITWISE_NOT) == 0)
206219
{
207-
emit_raw("not unsigned(");
220+
emit_raw("std_logic_vector(not unsigned(");
208221
node_generator(inner_expression);
209-
emit_raw(")");
222+
emit_raw("))");
210223
}
211224
else
212225
{

src/codegen/codegen_vhdl_main.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,15 @@ static void generate_node(ASTNode *node)
9797
}
9898
}
9999

100-
// Emit VHDL library imports and struct type declarations, then generate
100+
// Emit VHDL struct type declarations, then generate
101101
// each function as an independent entity/architecture pair.
102+
// Library/use clauses are emitted per-entity so that every design unit
103+
// in the file has the required imports (VHDL context clauses apply only
104+
// to the immediately following design unit).
102105
static void generate_program(ASTNode *node)
103106
{
104-
// Emit VHDL header
105107
emit_line("-- VHDL generated by gates");
106108
emit_newline();
107-
emit_line("library IEEE;");
108-
emit_line("use IEEE.STD_LOGIC_1164.ALL;");
109-
emit_line("use IEEE.NUMERIC_STD.ALL;");
110-
emit_newline();
111109

112110
// Emit struct type declarations
113111
emit_all_struct_declarations();
@@ -159,6 +157,11 @@ static void emit_entity_declaration(const char *function_name,
159157
ASTNode *parameters[], int parameter_count,
160158
ASTNode *node)
161159
{
160+
// Each design unit requires its own library/use context clause
161+
emit_line("library IEEE;");
162+
emit_line("use IEEE.STD_LOGIC_1164.ALL;");
163+
emit_line("use IEEE.NUMERIC_STD.ALL;");
164+
emit_newline();
162165
emit_line("-- Function: %s", function_name);
163166
emit_indented("entity ");
164167
emit_safe_identifier(function_name);

0 commit comments

Comments
 (0)