Building a C-like Compiler and VHDL Microprocessor

This project was a true crash course in bridging the gap between theory and practice. As part of my studies at INSA Toulouse, I set out to design both a compiler for a simplified C-like language and a custom VHDL microprocessor capable of executing the generated machine code. It was the first time I had to take an abstract idea—"make a language run on a processor"—and actually implement the entire chain.

The Challenge

At first, the task looked overwhelming: three moving parts that all had to work together. On one side, the compiler had to translate human-readable code into structured instructions. On the other, a microprocessor needed to understand and execute those instructions. And between them was the delicate interface: assembly code that had to be syntactically precise and semantically sound.

Compiler: From Text to Instructions

I began with the compiler, which we split into two classic stages: lexical analysis and syntax analysis.

Lexical Analysis with Flex

Using Flex, I built a lexical analyzer that could recognize tokens such as keywords (if, while), identifiers, numbers (including hexadecimal and scientific notation), and operators (+, -, *). It also handled comments gracefully and threw errors for unknown tokens. Running the lexer felt like watching raw code come alive, structured into pieces ready for deeper analysis.

Syntax Analysis with Bison

The syntax analyzer, written with Bison, checked these tokens against grammar rules. It supported function declarations, arithmetic expressions, conditionals, and loops. To keep everything consistent, I used a symbol table to track variables and functions, while an assembly table produced machine-ready instructions. This was where the compiler truly "spoke" to the hardware level.

Microprocessor: Bringing Code to Life

With the compiler generating assembly, the next step was to build a processor that could run it. Designed in VHDL, my microprocessor followed a RISC 5-stage pipeline architecture. It included:

For me, the breakthrough moment was watching my own assembly instructions actually run in a Vivado simulation—proof that the compiler and microprocessor were finally in sync.

Lessons Learned

This project taught me that building a system end-to-end is more than just coding each part correctly. It's about designing clear interfaces between layers, and anticipating how design choices in one layer ripple down into the others. Debugging across both the compiler and the processor forced me to think systematically, which is a skill I now carry into my work in cybersecurity.

View on GitHub

Figures

Table showing the implemented instructions
Figure 1: Instruction set implemented for the microprocessor.
Pipeline architecture diagram
Figure 2: RISC 5-stage pipeline architecture in VHDL.
Vivado simulation of the microprocessor
Figure 3: Simulation of machine code execution in Vivado.