Lucid languauge question about dff

While trying to learn both Lucid and System Verilog at the same time, a question popped up: why is the D-flipflop not simply implemented explicitly as a module named dff instead of as a sort of “built-in” language construct? I understand that it does not make any difference for the end result but I find that it kind of “pollutes” the generated Verilog code.

If it’s a question of ergonomics, you could just silently add the dff module as soon as one is used in a project. Same goes for other flip-flop types (SR, JK, T).

I find Lucid a lot easier to write than Verilog, but this confused me.

It was done this way because this is how designs are often. I was originally introduced to the style of having an @* and an @(posedge clk) where the first had all your combinational logic and the second having just the DFFs during my internship at Northrop Grumman.

They enforced this style to prevent mistakes like accidentally creating latches which is important but I hated it since every DFF always showed up in three places (declaration, default values in combo block, and q <= d / reset in sync block). Removing the redundancy while keeping this strict style was the original inspiration for Lucid.

FPGAs don’t have other types of flip-flops in them so there isn’t really a point to supporting them. If you’re curious, you can see this doc for details about the inside of the Au’s FPGA.

You’ll often seen Verilog written in a looser style where you just have a single always block that uses @(posedge clk). You can then mix = and <= assignments for blocking (signals) and non-blocking (DFF) assignments. While this style is nice and compact, it is error prone and often hard to see/debug small typos.

For what it’s worth, until recently the generated Verilog was very unreadable. By switching to SystemVerilog, there are now features like packed arrays and packed structs that closely match Lucid. These used to be implemented as a single array where all the indexing was handled during translation making an unreadable mess. You’ll still see this when using the open source tools for the Cu which don’t properly support SystemVerilog.

Thanks for taking the the time to explain why it is the way it is. Much appreciated!

Edit: the way one writes Lucid code also prevents one from introducing latches, am I right? Yes I am, as it says so in your answer:)

Yeah Lucid doesn’t let you write synchronous code so you can’t make a latch (by design). The biggest gotcha I still see people make with it is trying to be clever with clocks. You should clock all the DFFs with the same clock and if you can’t for some reason, you need to take care crossing the clock domains.