Does modeling digital circuits in C have any practical benefits as opposed using the language's standard operations?

StackOverflow https://stackoverflow.com/questions/20984906

  •  25-09-2022
  •  | 
  •  

Question

So I've start looking into digital circuit designs and enlightened to find that almost every operation (that I'm aware of), all derive from 3 logical operations: AND, OR, and NOT. As an analogy, these are sort of like subatomic particles to atoms that make up everything else. Subatomic particles are to logic gate as atoms are to processor instructions. If programming in Assembly is like putting atoms together, then programming in C is like putting molecules (and atoms) together. Someone PLEASE tell me if I'm off base here.

With that said, I'm aware that GCC and most other compilers do a pretty good job optimizing from C to machine code. Lets assume we are looking at an x386 instruction set. If I built an 32-bit full-adder using only $$, ||, and ~, is the compiler smart enough to use existing instructions provided by the processor, or will my full-adder end up being a more bloated, less efficient versions of what's already on the processor.

Disclaimer: I started looking into digital circuits in an attempt to start learning assembly, and I'm fair in C. I want to model some of these circuits in C to further my understand of the digital circuits because those are in terms I understand. But I don't want to lure myself into the illusion that it will also be efficient code (or any other practical benefits other than learning) when using a simple + will do. And yes, I'm aware that the horrible maintainability of the code would far outweigh any benefit's of this coding "style" may provide.

Was it helpful?

Solution 2

I've always been of mind that building digital circuits is no different than programming in assembly. I've said before that electronics are like physical opcodes.

To you question of C smart-compiling a logic adder to an ADD instruction.. No, it will not. There are a few exceptions in embedded development such as AVR and avr-gcc with the right optimization flags will turn REGISTER|=1<<bit or REGISTER&=~(1<<bit) and turn those into bit set and clear instructions instead of doing a verbatim Load, Logic, Store.

Beyond assemble/opcodes I can't really think of an analogy for higher-level languages to electronics.

OTHER TIPS

A software simulation of a full adder will never be anywhere near as efficient than a full adder built out of logic gates of the same technology as the silicon which runs the software simulation. The number of gates involved in running the simulation will far outstrip the gate count in the hardware adder, and the propagation delays will be significantly longer, especially if you include the I/O processing needed to make the simulation function as a real adder which accepts and produces electronic signals, which means that the code has to read and write actual CPU or peripheral I/O pins.

The above is true even if a crack team of the world's best x86 assembly language coders get together in a conference to design the ideal piece of code to implement a full adder in machine language; in other words, it doesn't reflect the inability of compilers to optimize C sufficiently well.

A software simulation of a logic circuit on a some given computer, however, can be more efficient than a circuit built with some different technology from that computer: specifically, older technology. For instance, a program running on a modern, fast microcontroller chip with integrated I/O can likely express a faster adder on its GPIO pins, than something cobbed together out of discrete, through-hole transistors, and it will almost certainly take up less space, and possibly require less current.

Although you may use C to describe a digital circuit (in fact, the Verilog HDL has some resembling to C), there's one fundamental thing that you cannot model in plain C: parallelism.

If there's anything that belong to digital circuit description is its inherent parallelism in the description itself: functions (modules actually), and even code blocks "execute" in parallel.

Any ordinary C program describes a sequence of operations over time, and therefore, any attempt to describe a digital operation, unless it is a very trivial one, will be compiled as a sequence of steps, not exploiting the parallelism that digital circuits have by nature.

That said, there does exist some HDL (hardware description languages) that are fair close to C. One of them is Handel-C. Handel-C uses syntax borrowed from C, plus some additions to better handle the inherent parallelism present in digital design.

For example: imagine you have to exchange the value of two variables. The classical solution (besides solutions based in bitwise operations and the like) is:

temp = a;
a = b;
b = temp;

However, when someone is learning computer programming, it's a common mistake to code the above sequence as this:

a = b;
b = a;

Because we think in a variable interchange as a parallel operation: "the value of b is copied to a, meanwhile the value of a is copied to b".

The funny thing about this approach is that it actually works... if we manage to execute these two assignments in parallel. Something that is not possible in plain C, but it is in Handel-C:

par
{
  a = b;
  b = a;
}

The par statement indicates that each code line is to be "executed" in parallel with respect to the others.

In Verilog, the same interchange would be written as this:

a <= b;
b <= a;

<= is the nonblocking assignment in Verilog: the second line is not "executed" after the first one is finished, but both start at the same time. This sequence is normally found inside a clocked always (sort of a loop that is "executed" every time the clock signal in the sensitivity list changes from 0 to 1 -posedge- or from 1 to 0 -negedge- ).

always @(posedge clk) begin
  a <= b;
  b <= a;
end

This means: everytime the clock goes from 0 to 1, interchange the values between a and b.

Note that I always quote "executed" when I speak about languages for digital design. The code doesn't actually translate into a sequence of operations to be executed by a processor, but the code IS a circuit. Think of it as a 1D rendering of a 2D schematic, with sentences and operators instead of electronic symbols, and assignments, arguments and "function calls" instead of wires.

If you are familiarized with digital circuits, you will realize that the "always" loop look-alike, is actually translated into this:

Two flip flops with outputs connected to each other inputs

Which is something you couldn't do with just translating the same high level description into assembly (unless the ISA of the target processor has some sort of XCHG instruction, which is actually not uncommon, and the code keeps the two variables to be interchanged into CPU registers).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top