Lucid V2

I’m currently working the new parser for Lucid V2. This will be a major update to the Lucid language and will bring many breaking changes.

Currently, these are the changes I’m working on.
[]Optional semicolons. Following the trend of many modern languages, new lines can be used in place of semicolons.
]Removal the ‘var’ type. This type never really made much use sense to me. It is functionally equivalent to ‘sig’ that is 32 bits wide.
[*]Replace the ‘for’ loop with a ‘repeat’ statement. This will likely have the syntax like “repeat(5: sig) { }” where “sig” would be assigned 0,1,2,3,4 depending on the iteration. For statements are kind of a weird thing to have in hardware since they must have a fixed number of iterations. The “C” style loop used currently and by Verilog is a bit cumbersome and can easily be written to not be a fixed number of iterations.
I’d love some feedback on these changes and any other potential changes you’d like to see make it into this update.

The rest of this post is the nitty gritty of the update.

The original Lucid parser has had feature after feature tacked on to where it is a bit of a mess today. Originally, it only attempted to figure out the width of signals. Then I added stuff that would parse constant expressions so custom functions could be added. Now I want a general interpreter that could be used to build a simulator. This was the motivation behind the full overhaul.

The original parser is broken into a handful of modules here

The main parent is the LucidExtractor. This deals with all the declarations of stuff like modules, dffs, fsms, etc.
The BitWidthChecker attempts to figure out how wide each signal is. It is responsible for giving errors for situations like when you try to assign a struct to an array.
The ConstExprParser was original used to parse constant expressions for functions but has slowly outgrown that role to general expression parsing.
The BoundsParser parses array bounds (like [5:1] or [0+:3])
The ConstParser builds a list of constants declared
The ParamsParser builds a list of parameters declared

There are also a handful of other helper parsers like the LucidGlobalExtractor that parses globals in an initial pass of all the files.

These are all weirdly interdependent which is one of the goals to fix for the new parse.

Currently, the V2 parser handles expressions. I now need to add the rest (variable declarations, various block parsing, global statements, etc)

Its code can be found here
The ExprParser is responsible for assigning a Value to every expression. This combines the functionality of the BitWidthChecker and ConstExprParser as these were often redundant. The Value type has an associated width to it and can be an UndefinedValue that still has a width which covers the cases that the BitWidthChecker used to deal with when ConstExprParser couldn’t figure out a value.

I also plan to roll some of the small parsers into ExprParser such as BoundsParser (which I added today). Having multiple parsers that are interdependent makes using them difficult.

I can’t say I am very familiar with lucid, but I am with other, non-hdl languages, and some verilog. The removal of ‘var’ makes sense - this seems like a somewhat pointless abstraction that might be misused, and might lead to less understanding of what is actually going on under the hood.
Changing ‘for’ to ‘repeat’ is also great - coming from conventional programming, ‘for’ is a sequential operation. Renaming it should help separate hdl loops from conventional ones. Personally, something such as “repeat(sig: 0…5) { }” would be preferred, as it can start from an index and end at one. When reading “repeat(5: sig) { }” for the first time, I assumed the value went from 5, incrementing to the value of ‘sig’. Not sure if verilog supports initial values in loops natively, but I imagine one could increment from 0 to (b - a) assuming “repeat(sig: a…b)” - with ‘sig’ equal to the incrementing value added to a. I hope that makes sense.

On semicolons: Please, mandate them. Newline-blindness is something I find helpful, and allows splitting up long lines of code. Python’s lack of semicolons with ‘:’ and indent-sensitivity… I don’t like it. I assume relying on newlines can also present issues with differing unix/windows/etc line ending sequences, and tab/space differences. I can also see the benefit to new programmers, however.

Hope this feedback is helpful,

Thanks for the feedback Mark.

What do you think about making repeat more like a function? Something like repeat(sig, 0, 5){}. It could also have an optional step size as a fourth argument. The “…” range syntax just seems a bit out of place (as does the colon) since it isn’t used anywhere else.

The way I changed the grammar, it is still largely indifferent to newlines. Basically, you can still put them whenever you want to split long lines up. Whitespace is also ignored so tabs/spaces don’t matter. The only difference is where a semicolon would’ve been required before, a newline is also sufficient. The rules also account for various newline styles across OS’s.

Repeat as a function sounds wonderful - personally, I am not a fan of step size, as a user can just multiply the output signal by n, and keep the function more simple. I could see performance benefits to step size args in HDL - in my limited reading, combinational single-clock multipliers are VERY expensive in terms of LUTs. As I assume loop counters are incremented via an adder, the step size could just be n+x instead of n+1, *x. If my line of thinking is completely wrong, sorry.
Your comment on being sure to use non-unique syntax is wise.
The reassurance on semicolons is very helpful, and has eased my worries. It’s a pet peeve of mine I really need to get over.

It’s awesome to see you asking for community feedback!

Any word on an HDMI shield release date?

For loops in hardware are equivalent to copy pasting everything N times. There isn’t actually any counter or multiplication that gets done. You could just use multiplication to change the step size and the tools should calculate the values all at synthesis time.

I like having the step as an option for the weird case when maybe going backwards is convenient. The number of iterations will be checked to ensure you don’t do something like 0 to 5 with -1 steps (you’d want 5 to 0 with -1 steps).

I’m waiting for some parts to finally build the HDMI prototype I’ve had on my desk. I think they’re supposed to be back in stock March.

Thanks for the update, and tolerating my lack of HDL knowledge. Your point about step size makes sense. Best of luck!

*** Currently, the V2 parser handles expressions. I now need to add the rest (variable declarations, various block parsing, global statements, etc) ***

Is this why I cannot pass data from the uart into a case or if statement in the top module???

How do I install this version 2 (Lucidv2)? Without a MS Installer attached to it, I don’t know how to do it. I would like to try it where it is now. People at work have been counting on me to get what I thought should be a simple FPGA solution working by now.

Any news on Lucid V2?

I’m not sure what you mean by pass data from the uart into a case/if statement. This should be 100% possible currently.

Lucid V2 isn’t released in any form yet. It is still being worked on.

Have you tried DDR3 tutorial? I have copied code, what the tutorial description says to this forum and two months later I get this; disappointing. The tutorial claims I can substitute any byte (character) to the memory location. This is only true if the byte is created within the case statement. I cannot create variable or take a byte from the UART, for example, and store it to memory; it will not even show the byte (these are regular ASCII characters, A-Z, a-z, 0-9) if I try to display it from within the case statement. If there is real (timely) interest in solving this for me and you, I will create a small routine you can run to see what I am talking about.