VLSIChaps Academy · Track 01

Functions, Computation With No Clock

~10 min Hands-on lab 1 exercise Verilator
Page 15 of 50
01

The Instant Routine

A function is a named routine that takes inputs, computes, and hands back a value, all in zero simulated time. Where the previous page's tasks were built to live in time, driving, waiting, holding, a function is built to be instant: call it, and the answer exists in the same moment, no delay, no clock, no waiting. That instancy is not a limitation, it is the contract. When you call a function you are asking a question, not starting an activity, and the language holds you to it.

02

Time Is Illegal Here

The contract is enforced. A delay inside a function is illegal, a wait is illegal, and calling a time consuming task from inside a function is illegal, because any of them would make the instant routine take time, and the compiler refuses outright. This is not a style rule that lint politely flags; it is a hard error, and in this lab you can see it for yourself: put a delay inside your function, and the engine will stop you at compile with the exact line. The boundary is worth memorising as a sentence: if the job involves time, it is a task, the routine that lives in time; if the job is pure computation, it is a function.

03

Purity, the Quiet Superpower

The best functions read only their arguments and touch nothing outside themselves. Same inputs, same answer, every single call, no matter what the rest of the testbench is doing. That property is called purity, and it is why functions become the reference brain of a testbench: when you need to know what the design should have produced, you ask a pure function, precisely because its answer cannot be contaminated by anything else. Hold that thought, because the primer's final page builds a self checking testbench, and the checker at its heart is exactly this kind of function. One habit to adopt now: declare your functions automatic, the same one word you learned on the tasks page, so every call carries its own private storage and the function stays independent no matter how it is used.

04

Key Insight: The Return Type You Forgot

Here is the trap that produces wrong answers with no error at all. A function's return type is part of its declaration, and if you leave it out, the language does not object, it quietly defaults the return to a single bit. Your function then computes a perfectly correct wide answer and hands back one bit of it. Count eight ones in a byte, and a one bit return delivers zero, because eight in binary is a one followed by three zeros and only the lowest bit survives. You met this class of bug on the vectors page, where a value that does not fit is silently truncated: the value simply wrong from that point on. The rule is the same as it was there: count the bits your answer needs, and declare exactly that many on the return. This lab's checker calls your function with values chosen to expose a forgotten or narrow return the moment it happens.

Lab: Count the Ones, Instantly

Write a function named count_ones that takes an eight bit value and returns how many of its bits are one. The answer can be as large as eight, so choose a return type wide enough to hold it. Use a loop to walk the bits, the forms from the loops page all work. The check section below your function is already written and must not be modified: it calls count_ones on five known bytes and prints each result. Run and submit. A missing or narrow return type will corrupt specific lines, and the engine will name them; a delay inside the function will not even compile.

Three decisions and the function writes itself. The return type: the count can reach eight, and eight needs four bits. The walk: any loop from the loops page that visits all eight bit positions. The delivery: add one to a running count for each set bit, and return the count at the end. And remember what is not allowed in here: no delays, no waits, this routine answers instantly or not at all.
Compiling, linting, and simulating...
Correct. Five calls, five instant answers, and a return type wide enough that the largest count arrived intact. You have now written both kinds of routine: the task that lives in time, and the function that is forbidden from it. Keep this one pure, arguments in, answer out, nothing else touched, and it will serve as the reference brain of every checker you build from here, starting with the one at the end of this primer.
Not quite. Your function ran and answered instantly, but specific answers are wrong, and the pattern of which ones matters. If the large counts collapsed while the small ones survived, your return type is truncating the answer, the same silent bit dropping you met on the vectors page. The engine output and the mentor below name the lines and the width to fix.
Did not compile. Your function did not compile, and this failure is the lesson: you asked a function to consume time, and time is illegal inside a function, not discouraged, illegal. The engine's error below names the line. If this computation truly needs to wait, it belongs in a task; if it does not, remove the delay and let the function answer in the instant it was asked.
Compile error.
Something went wrong reaching the engine. Try again in a moment.
AI Mentor
Thinking...
📲 Join 20K+ Engineers